[NOIP模擬][dp][codevs]p1401逆序對統計
我們說(i,j)是a1,a2,…,an的一個逆序對當且僅當i<j且ai>aj。例如2,4,1,3,5的逆序對有3個,分別為(1,3),(2,3),(2,4)。現在已知N和K,求1..N的所有特定排列,這些排列的逆序對的數量恰好為K。輸出這些特定排列的數量。
例如N=5,K=3的時候,滿足條件的排列有15個,它們是:
1,2,5,4,3 1,3,4,5,2 1,3,5,2,4 1,4,2,5,3 1,4,3,2,5
1,5,2,3,4 2,1,4,5,3 2,1,5,3,4 2,3,1,5,4 2,3,4,1,5
2,4,1,3,5 3,1,2,5,4 3,1,4,2,5 3,2,1,4,5 4,1,2,3,5
輸入描述 Input Description輸入第一行有兩個整數N和K。其中(N<=100,K<=N*(N-1)/2)
輸出描述 Output Description
將1..N的逆序對數量為K的特定排列的數量輸出,為了避免高精度計算,請將結果mod10000後再輸出
樣例輸入 Sample Input【樣例輸入1】
5 3
【樣例輸入2】
10 7
【樣例輸入3】
10 10
樣例輸出 Sample Output【樣例輸出1】
15
【樣例輸出2】
4489
【樣例輸出3】
1670
方程很容易得出:f[i][j]=f[i-1][j-i+1]+f[i-1][j-i+2]+……+f[i-1][j]但是若果這樣是O(n^3)
可以優化為O(n^2)
f[i][j]=f[i-1][j-i+1]+f[i-1][j-i+2]+……+f[i-1][j]
f[i][j-1]=f[i-1][j-i]+f[i-1][j-i+1]+f[i-][j-i+2]+……+f[i-1][j-1]
這兩個式子有許多重複項
所以可以合併為f[i][j]=f[i][j-1]+f[i-1][j]-f[i-1][j-i]
程式碼:
#include<iostream>
#include<cstdio>
#include<ctime>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<string>
#include<set>
#include<map>
#include<vector>
#include<queue>
#include<algorithm>
#ifdef WIN32
#define AUTO "%I64d"
#else
#define AUTO "%lld"
#endif
#define INF 0x3f3f3f3f
#define clock CLOCKS_PER_SEC
#define cle(x) memset(x,0,sizeof(x))
#define maxcle(x) memset(x,127,sizeof(x))
#define mincle(x) memset(x,-1,sizeof(x))
#define cop(a,x) memcpy(x,a,sizeof(a))
#define FROP "codvs"
#define C(a,b) next_permutation(a,b)
#define LL long long
using namespace std;
const int N=105,mod=1e4;
int n,k,f[N*N/2][N];
int main()
{
freopen(FROP".in","r",stdin);
freopen(FROP".out","w",stdout);
scanf("%d%d",&n,&k);
for(int i = 0 ; i <= n; i++)
f[0][i]=1;
for(int i = 1; i <= n; i++)
for(int j = 1; j <= k; j++)
{
f[j][i]=(f[j][i-1]+f[j-1][i])%mod;
if(j>=i)f[j][i]=(f[j][i]-f[j-i][i-1]+mod)%mod;//!!!!!!!1減後防止為負數,,而且j>=i!!!!!!
}
printf("%d",f[k][n]);
return 0;
}
1.j>=i,,陣列下標運算時,不能為負數。。。!!!!!!!!注意判斷,,,這個遞推式子。。
2.當要模一個數的時候,,,特別當有減的時候,,一定要加上這個數在減去在模,防止模後又減去未負數!!!!!!!!!
if(j>=i)f[j][i]=(f[j][i]-f[j-i][i-1]+mod)%mod;///////////!!!!!!!!!!!!!!