【题目描述】
对于一个数列{a[]},如果有i<j且a[i]>a[j],那么我们称a[i]与a[j]为一对逆序对数。若对于任意一个由1~n自然数组成的数列,可以很容易求出有多少个逆序对数。询问逆序对数为k的这样自然数数列到底有多少个。
【输入描述】
第一行为两个整数n、k。
【输出描述】
写入一个整数,表示符合条件的数列个数,由于这个数可能很大,你只需输出该数对10000求余数后的结果。
【输入样例】
4 1
【输出样例】
3
【数据范围】
下列3个数列逆序对数都为1,分别是:
1 2 4 3
1 3 2 4
2 1 3 4
n <= 1000,k <= 1000。
BZOJ的数据太水了,O(m*n^2)的普通解法竟然过了:
源代码: #include<cstdio> int m,n,f[1001][1001]={0}; int main() { scanf("%d%d",&n,&m); for (int a=1;a<=n;a++) //初始化边界。 f[a][0]=1; for (int a=1;a<=n;a++) for (int b=1;b<=m;b++) for (int c=1;c<=a;c++) if (b-a+c>=0) //避免越界溢出。 f[a][b]=(f[a][b]+f[a-1][b-a+c])%10000; printf("%d",f[n][m]); return 0; } /* 挺简单的DP: f[i][j]表示1~i个自然数所组合成的、逆序对数为j的数列个数。 在1~i中,i即为最大,故将它安插到某一位置,其后方的所有数都会与它形成逆序对。 由上可得状态转移方程: f[i][j] = ∑f[i-1][j-i+k],(1 <= k <= i)。 */
滚动数组+前缀和优化后的O(mn)解法:
源代码: #include<cstdio> int m,n,sum[1001],f[1001]; int main() { scanf("%d%d",&n,&m); for (int a=0;a<=m;a++) //除f[1][0]为1外,f[1][j]皆为0。 sum[a]=1; for (int a=2;a<=n;a++) //直接从f[2][]开始。 { f[0]=1; //f[i][0]显然为1。 for (int b=1;b<=m;b++) if (b-a>=0) { f[b]=sum[b]-sum[b-a]; while (f[b]<0) //注意此处理,避免因负数而出现错误。 f[b]+=10000; } else f[b]=sum[b]; for (int b=1;b<=m;b++) //前缀和处理。 sum[b]=(sum[b-1]+f[b])%10000; } printf("%d",f[m]); return 0; }