[BZOJ] 2431 逆序对数列

Time Limit: 5 Sec  Memory Limit: 128 MB
Submit: 2611  Solved: 1526
[Submit][Status][Discuss]
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
HINT
Source
Day1

先想到区间dp,发现只记录前缀就行,所以二维就可以解决。
f[i][j],前i个数,j个逆序对的方案数。
对于新加入的i+1,可以造成i+1种逆序对,所以枚举前面的就行了。

先写的暴力版本,TLE两个点,一算10000*1000没爆int,把mod放外面,快了不少,过了一个点,然后循环展开,不开o2也跑得飞快(相较朴素暴力…)

#include<iostream>
#include<cstdio>

using namespace std;

int n,k;

int f[1005][1005]={1};

int main(){
    scanf("%d%d",&n,&k);
    for(register int i=1;i<=n;i++){
        for(register int j=0;j<=k;j++){
            int l=0;
            for(l=0;l<i-8;l+=8){    
                if(j<l) continue;
                f[i][j]+=f[i-1][j-l];
                if(j<l+1) continue;
                f[i][j]+=f[i-1][j-l-1];
                if(j<l+2) continue;
                f[i][j]+=f[i-1][j-l-2];
                if(j<l+3) continue;
                f[i][j]+=f[i-1][j-l-3];
                if(j<l+4) continue;
                f[i][j]+=f[i-1][j-l-4];
                if(j<l+5) continue;
                f[i][j]+=f[i-1][j-l-5];
                if(j<l+6) continue;
                f[i][j]+=f[i-1][j-l-6];
                if(j<l+7) continue;
                f[i][j]+=f[i-1][j-l-7];
            }
            for(l;l<i;l++){
                if(j<l) continue;
                f[i][j]+=f[i-1][j-l];
            }
            f[i][j]%=10000;
        }
    }
    printf("%d",f[n][k]);
    return 0;
}

正解是前缀和优化,每次更新都是加一段连续区间的值,可以用前缀和降复杂度。

#include<iostream>
#include<cstdio>

using namespace std;

const int MAXN=1005;
const int MOD=10000;

int dp[MAXN][MAXN];
int n,k,ans,sum;
int main()
{
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++) dp[i][0]=1;
    for(int i=2;i<=n;i++)
    {
        sum=0;
        for(int j=0;j<=k;j++)
        {
            (sum+=dp[i-1][j])%MOD;
            dp[i][j]=sum%MOD;
            if(j-i+1>=0)((sum-=dp[i-1][j-i+1])+=MOD)%MOD;
        }
    }
    printf("%d\n",dp[n][k]);
    return 0;
}
posted @ 2018-06-04 17:18  GhostCai  阅读(168)  评论(0编辑  收藏  举报