【题解】逆序排列 [51nod1020]

【题解】逆序排列 [51nod1020]

传送门:逆序排列 ([51nod1020])

【题目描述】

(T) 组测试点,每一组给出 (2) 个整数 (n)(k),在 ([1,n])(n) 个数字的全排列中,逆序数为 (k) 的排列种数,答案对 (1e9+7) 取模。

【样例】

样例输入:
1
4 3

样例输出:
6

【数据范围】

(100\%) (1 leqslant T leqslant 10000,) (1 leqslant N leqslant 1000,) (1 leqslant K leqslant 20000)

【分析】

(dp[i][j]) 表示 ([1,i]) 的排列中逆序对数为 (j) 的排列种数,为方便枚举,用 (ss[i]) 表示长度为 (i) 的排列总数((ss[i]=min{K,frac{i*(i-1)}{2}})

对于数字 (i) 来说,在一个 ([1,i-1]) 的排列中它有 (i) 个位置可以加入,加下 (i) 之后新构成的逆序对数 (kin [0,i-1])
那么 (dp) 方程就出来了:(dp[i][j]+=dp[i-1][j-k]),其中 (0 leqslant j leqslant ss[i],) (0 leqslant j-k leqslant ss[i-1]) ,所以 (max{0,j-ss[i-1]} leqslant k leqslant min{j,i-1})
(dp[i][j]=sum_{k=max{0,j-ss[i-1]}}^{min{j,i-1}}dp[i-1][j-k])

时间复杂度过高,要用前缀和优化一下,(dp[i][j]=S[j-max(0,j-ss[i-1])]-S[j-min(i-1,j)-1]),其中 (S[i]=sum_{j=0}^{i}dp[i-1][j])

但是前缀和下边界可能会取到 (0),减了 (1) 之后就成了 (-1)(S[-1])是不存在的,所以每一次写 (S[x]) 都要改成 (S[x+1])

还要先预处理答案,询问时直接 (O(1)) 查询。

时间复杂度为 (O(NK))

【Code】

#include<algorithm>
#include<cstring>
#include<cstdio>
#define LL long long
#define Re register int
using namespace std;
const int N=1003,M=2e4+3,P=1e9+7;
int T,n,K,S[M],ss[N],dp[N][M];
inline void in(Re &x){
    int f=0;x=0;char c=getchar();
    while(c<'0'||c>'9')f|=c=='-',c=getchar();
    while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
    x=f?-x:x;
}
int main(){
    n=1000,K=20000;
    for(Re i=2;i<=n;++i){
    	ss[i]=i*(i-1)>>1;
    	if(ss[i]>K){for(Re j=i;j<=n;++j)ss[j]=K;break;}
    }
    for(Re i=1;i<=n;++i)dp[i][0]=1;
    for(Re j=0;j<=ss[2];++j)(S[j+1]=S[j-1+1]+dp[1][j])%=P;
    for(Re i=2;i<=n;++i){
    	for(Re j=1;j<=ss[i];++j)(dp[i][j]=(S[j-max(0,j-ss[i-1])+1]-S[j-min(i-1,j)-1+1]+P)%P)%=P;
    	for(Re j=0;j<=ss[i+1];++j)(S[j+1]=S[j-1+1]+dp[i][j])%=P;
    }
    in(T);while(T--)in(n),in(K),printf("%d
",dp[n][K]);
}
原文地址:https://www.cnblogs.com/Xing-Ling/p/11347772.html