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;
}

 

posted @ 2018-03-04 20:59  Star_Feel  阅读(178)  评论(0编辑  收藏  举报