【DP计划】11.4——[BZOJ]逆序对数列(前缀和优化DP)EXTREMELY EASY

Description
对于一个数列{ai},如果有iaj,那么我们称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


我们只要知道怎么构筑这个排列即可。
假设我们现在拥有一个从1到n的正序排列,那么它的逆序对数为0。那么从左向右每次将某一个数左移x位,那么就会产生x个逆序对。因此设 f [ i ] [ j ] f[i][j] f[i][j]表示当前已经到了第 i i i位,已经左移了 j j j位(也就是产生了 j j j个逆序对)的方案数,那么转移方程就是
f[i][j]+=f[i-1][k]   //k∈[j-i+1,j] (因为位置为i-1的点最多只能左移i-2位)
于是直接暴力转移是 O ( n 3 ) O(n^3) O(n3)的。但是显然 k k k属于一段区间,因此可以进行前缀和优化,及对于 f [ i − 1 ] f[i-1] f[i1]计算前缀和即可。于是复杂度变为 O ( n 2 ) O(n^2) O(n2)
#include
#define MD 10000
using namespace std;
int read(){
    char c;int x;while(c=getchar(),c<'0'||c>'9');x=c-'0';
    while(c=getchar(),c>='0'&&c<='9') x=x*10+c-'0';return x;
}
int n,k,f[1005][1005],sum[1005];
int main()
{
    n=read();k=read();
    f[1][0]=1;for(int i=0;i<=k;i++) sum[i]=1;
    for(int i=2;i<=n;i++){
        f[i][0]=1;
        for(int j=1;j<=k;j++){
            int x=max(0,j-i+1)-1;
            (f[i][j]+=sum[j]-sum[x])%=MD;
        }
        for(int j=0;j<=k;j++) sum[j]=sum[j-1]+f[i][j];
    }
    printf("%d",f[n][k]);
    return 0;
}

你可能感兴趣的:(BZOJ,DP计划)