[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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章