51Nod 1020 逆序排列
Posted 心之所向 素履以往
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了51Nod 1020 逆序排列相关的知识,希望对你有一定的参考价值。
在一个排列中,如果一对数的前后位置与大小顺序相反,即前面的数大于后面的数,那么它们就称为一个逆序。一个排列中逆序的总数就称为这个排列的逆序数。
如2 4 3 1中,2 1,4 3,4 1,3 1是逆序,逆序数是4。
1-n的全排列中,逆序数最小为0(正序),最大为n*(n-1) / 2(倒序)
给出2个数n和k,求1-n的全排列中,逆序数为k的排列有多少种?
例如:n = 4 k = 3。
1 2 3 4的排列中逆序为3的共有6个,分别是:
1 4 3 2
2 3 4 1
2 4 1 3
3 1 4 2
3 2 1 4
4 1 2 3
由于逆序排列的数量非常大,因此只需计算并输出该数 Mod 10^9 + 7的结果就可以了。
Input
第1行:一个数T,表示后面用作输入测试的数的数量。(1 <= T <= 10000) 第2 - T + 1行:每行2个数n,k。中间用空格分隔。(2 <= n <= 1000, 0 <= k <= 20000)
Output
共T行,对应逆序排列的数量 Mod (10^9 + 7)
Input示例
1 4 3
Output示例
6
dp+离线处理
我们很容易想到状态
dp[i][j] 代表在i个数的全排列中有j 的逆序数的方案数
下面我们来考虑这个状态的转移
显然将 i 插入 i-1 的排列中 不同的位置会产生不同的方案数
可能不产生逆序数 可能产生 1个 或 2个 或3个.....但最多能产生 i-1 个逆序数
于是 f[i][j]=f[i-1][j]+f[i-1][j-1]+f[i-1][j-2]+f[i-1][j-3]+f[i-1][j-4].........+f[i-1][j-(i-1)];
只有一个方程我们看不出怎么转移
我们 再找另一个方程 f[i][j-1];
f[i][j-1]=f[i-1][j-1]+f[i-1][j-1-1]+f[i-1][j-1-2]+f[i-1][j-1-2]...............f[i-1][j-1-(i-1)];
显然两个方程右边是有许多相同项的
我们用上式减去下式
得到 f[i][j]-f[i][j-1]=f[i-1][j]-f[i-1][j-1-(i-1)];
化简得到 f[i][j]=f[i][j-1]+f[i-1][j]-f[i-1][j-i];
于是就可以转移了
1 #include <cstdio> 2 #include <cctype> 3 4 const int mod=1000000007; 5 const int MAXN=20010; 6 7 int T,n,m; 8 9 int dp[1010][MAXN]; 10 11 inline void read(int&x) { 12 int f=1;register char c=getchar(); 13 for(x=0;!isdigit(c);c==‘-‘&&(f=-1),c=getchar()); 14 for(;isdigit(c);x=x*10+c-48,c=getchar()); 15 x=x*f; 16 } 17 18 int hh() { 19 read(T); 20 for(int i=1;i<=1000;++i) dp[i][0]=1; 21 for(int i=2;i<=1000;++i) 22 for(int j=1;j<=20000&&j<=i*(i-1)/2;++j) { 23 dp[i][j]=(dp[i][j-1]+dp[i-1][j])%mod; 24 if(j-i>=0) dp[i][j]=((dp[i][j]-dp[i-1][j-i])%mod+mod)%mod; 25 } 26 while(T--) { 27 read(n);read(m); 28 printf("%d\n",dp[n][m]); 29 } 30 return 0; 31 } 32 33 int sb=hh(); 34 int main(int argc,char**argv) {;}
以上是关于51Nod 1020 逆序排列的主要内容,如果未能解决你的问题,请参考以下文章