首页 > 代码库 > 【vijos】1757 逆序对(dp)

【vijos】1757 逆序对(dp)

https://vijos.org/p/1757

有时候自己sb真的是不好说。。。

我竟然想了半天都没想到这个转移。

我是有多傻。。。。

我们设f[i][j]表示1~i的排列且逆序对恰好是j的方案数。

显然我们只需要将i放进i-1排列中就行了。

而且发现i始终大于i-1

那么就好做了,我们只要将所有i放到i-1序列的位置的方案全都加起来即可,即:

f[i][j]=sum{f[i-1][k], max{0, j-i+1}<=k<=j}

用前缀和搞搞就行了。

#include <cstdio>#include <cstring>#include <cmath>#include <string>#include <iostream>#include <algorithm>#include <queue>using namespace std;#define rep(i, n) for(int i=0; i<(n); ++i)#define for1(i,a,n) for(int i=(a);i<=(n);++i)#define for2(i,a,n) for(int i=(a);i<(n);++i)#define for3(i,a,n) for(int i=(a);i>=(n);--i)#define for4(i,a,n) for(int i=(a);i>(n);--i)#define CC(i,a) memset(i,a,sizeof(i))#define read(a) a=getint()#define print(a) printf("%d", a)#define dbg(x) cout << (#x) << " = " << (x) << endl#define printarr2(a, b, c) for1(_, 1, b) { for1(__, 1, c) cout << a[_][__]; cout << endl; }#define printarr1(a, b) for1(_, 1, b) cout << a[_] << ‘\t‘; cout << endlinline const int getint() { int r=0, k=1; char c=getchar(); for(; c<‘0‘||c>‘9‘; c=getchar()) if(c==‘-‘) k=-1; for(; c>=‘0‘&&c<=‘9‘; c=getchar()) r=r*10+c-‘0‘; return k*r; }inline const int max(const int &a, const int &b) { return a>b?a:b; }inline const int min(const int &a, const int &b) { return a<b?a:b; }const int N=1005, MD=10000;int f[N], sum[N], n, k;int main() {	int cs=getint();	while(cs--) {		CC(f, 0); CC(sum, 0);		f[0]=1;		read(n); read(k);		for1(i, 1, n) {			for1(j, 1, k+1) sum[j]=sum[j-1]+f[j-1], sum[j]%=MD;			for1(j, 0, k) f[j]=(sum[j+1]-sum[max(0, j-i+1)]+MD)%MD;		}		printf("%d\n", f[k]);	}	return 0;}

  

 


 

 

描述

对于1-n的任意一个排列:a1,a2,a3...an,如果存在i<j,且ai>aj,则(i,j)称之为一对逆序对。

我们常常关心一个排列的逆序对的总数,因为它可以反映一个排列的有序程度。

现在小D想知道,在1-n的所有排列中,有多少排列的逆序对总数恰好为k。

格式

输入格式

第一行为正整数T,表示数据组数
接下来T行,每行两个正整数:n,k

输出格式

对于每个输入,输出一行表示恰好为k的排列的个数。由于数字可能较大,只需要输出mod10000的结果即可。

样例1

样例输入1[复制]

 
14 1

样例输出1[复制]

 
3

限制

每个测试点1s

提示

对于样例的解释,下面的排列满足条件:
1 2 4 3
1 3 2 4
2 1 3 4

对于30%的数据 n<=12;
对于100%的数据 n<=1000,k<=1000,T<=10;

【vijos】1757 逆序对(dp)