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