bzoj2431: [HAOI2009]逆序对数列

2431: [HAOI2009]逆序对数列

Description

对于一个数列{ai},如果有i<j且ai>aj,那么我们称ai与aj为一对逆序对数。若对于任意一个由1~n自然数组成的
数列,可以很容易求出有多少个逆序对数。那么逆序对数为k的这样自然数数列到底有多少个?

Input

第一行为两个整数n,k。

Output

写入一个整数,表示符合条件的数列个数,由于这个数可能很大,你只需输出该数对10000求余数后的结果。

Sample Input

4 1

Sample Output

3

样例说明:
下列3个数列逆序对数都为1;分别是1 2 4 3 ;1 3 2 4 ;2 1 3 4;
100%的数据 n<=1000,k<=1000

题解:(dp递推+优化)

稍微认真思考一下:

  假设给你若干个位置,如果把n个数从小到大一个个放进去(可以插入到放好的位置当中),对于当前放入的第i个数来说,它一定比前i-1个数都要大,所以当它在前i-1个位置中选择时,对于选择的任意一个位置都是有后效性的。

  定义f[i][j]表示用第i个数填(前i-1个位置)了之后,产生j对逆序对的方案数; 

  那么这时候我们发现,前i-1个数的方案数我们已经得出了,那么对于当前所要求的j对逆序对,我们可以直接从前面继承。每个数i要填时,后效性最少为0(填在最后),最大为i-1(最前面)。为0时我们就可以从f[i-1][j]继承,那么不难发现其他位置比如说填在第i-2个数的后面,就从f[i-1][j-1]继承。

  所以方程:f[i][j]=f[i][j]=∑f[i-1][j-k](0<=k<i)

  其实这样直接做会超时ORZ

  用个前缀和优化一下就好啦,时间复杂度降为O(n^2)...

 


 

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#define mod 10000
using namespace std;
inline int read()
{
    int f=1,x=0;char ch;
    while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0' && ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return f*x;
}
int n,k;
int f[1100][1100];//表示用第i个数填(前i-1个位置)了之后,产生j对逆序对的方案数; 
int main()
{
    n=read();k=read();
    memset(f,0,sizeof(f));
    
    f[0][0]=1;
    for(int i=1;i<=n;i++)f[i][0]=1;//产生0个逆序对的方案数仅有一种:上升序列
    
    int sum=0;
    for(int i=1;i<=n;i++)
    {
        sum=f[i-1][0];
        for(int j=1;j<=k;j++)
        {
            if(j-i>=0)sum-=f[i-1][j-i];
            sum+=f[i-1][j];
            f[i][j]+=sum;
            f[i][j]+=mod;
            if(f[i][j]>=mod)f[i][j]%=mod;
        }
    }
    
    printf("%d\n",f[n][k]);//输出填完n个数之后产生k个逆序对的方案数 
    return 0;
}

 

posted @ 2017-11-22 13:52  CHerish_OI  阅读(248)  评论(0编辑  收藏  举报