BZOJ2431: [HAOI2009]逆序对数列
【传送门:BZOJ2431】
简要题意:
求出一个有n个1到n的自然数的数列中有k个逆序对数的数列数
题解:
DP
设f[i][j]为前i个数有j个逆序对数的数列数
因为插入前i-1个数的时候方案已经得出了,我们需要插入第i个数获得新的一些序列。因为i比前面任何一个数都要大,所以插在第几位,就会比后面的数大,对于总逆序对产生贡献。考虑贡献大小,可以插在前i-1个数的最后面,那么对于逆序对数没有产生任何贡献,所以就是从f[i-1][j]转移过来,但是插在其他位置呢?如果插在i-2个数后面就是产生1的贡献,所以是f[i-1][j-1]。。。最后i-1中总共i-1个数,所以新产生的贡献最大为i-1。可以得到方程式:f[i][j]=∑f[i-1][j-k](0<=k<i)复杂度O(N^3)但是可以优化。因为每次用的是一段,所以前缀和优化一下,没必要每次重新统计。
以上为神犇解释
参考代码:
#include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #include<cstdlib> using namespace std; int f[1100][1100],s[1100][1100]; int main() { int n,k; scanf("%d%d",&n,&k); memset(f,0,sizeof(f)); for(int i=0;i<=n;i++) f[i][0]=1; for(int i=1;i<=n;i++) { s[i-1][0]=f[i-1][0]; for(int j=1;j<=k;j++) s[i-1][j]=(s[i-1][j-1]+f[i-1][j])%10000; for(int j=0;j<=k;j++) { if(j>=i) f[i][j]=(s[i-1][j]-s[i-1][j-i]+10000)%10000; else f[i][j]=s[i-1][j]; } } printf("%d\n",f[n][k]); return 0; }
渺渺时空,茫茫人海,与君相遇,幸甚幸甚