LOJ6077. 「2017 山东一轮集训 Day7」逆序对

给定 \(n,k\),求出长度为 \(n\) 的逆序对数恰好为 \(k\) 的排列的个数。答案对 \(10^9+7\) 取模。
\(1\le n,k\le 10^5, k\le \binom{n}{2}\)


考虑从小往大加入,当加入 \(n\) 时,逆序对数的增量 \(\Delta\text{pair} \in [0,n-1]\)

直接写出生成函数的表达式:

\[F(x)=(1+x)(1+x+x^2)\ldots(1+x+x^2+\ldots+x^{n-1})=\frac{\prod (1-x^i)}{(1-x)^n} \]

要求 \([x^k]F(x)\),将 \((1-x)^n\) 展开

\[F(x)=\prod(1-x^i)\sum_{i\ge 0}\binom{n+i-1}{i-1}x^i \]

考虑 \(\prod(1-x^i)\) 中的系数,显然可以看成 \(\{1,2,\ldots n\}\) 的和为 \(x\) 的子集个数。

那么设 \(f_{i,j}\) 表示选了 \(i\) 个数,和为 \(j\) 的方案数。由于数两两不同,则最多会有 \(\min(n,\sqrt{2k})\) 个不同的数。为了不计数重复,考虑以单调递增序列计数,转移时分以下 \(3\) 种情况:

  • 给所有数加 \(1\)\(f_{i,j}\leftarrow f_{i,j-i}\)
  • 在最前面加入一个 \(1\),其他数都加 \(1\)\(f_{i,j}\leftarrow f_{i-1,j-i}\)
  • 因为单调递增序列的末项不能 \(>n\),所以 \(f_{i,j}\leftarrow -f_{i-1,j-n-1}\)

最后和 \(\sum_{i\ge 0}\binom{n+i-1}{i-1}x^i\) 卷积即可。

总时间复杂度 \(O(n+k\sqrt{k})\)

\(\color{blue}{\text{code}}\)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int M=451,N=2e5+5,mod=1e9+7;
int n,k,ans,f[M][N];ll fac[N],inv[N],Inv[N];
inline void precalc(int n){
	fac[0]=inv[0]=Inv[0]=fac[1]=inv[1]=Inv[1]=1;
	for(int i=2;i<=n;++i)
		fac[i]=fac[i-1]*i%mod,
		Inv[i]=(mod-mod/i)*Inv[mod%i]%mod,
		inv[i]=inv[i-1]*Inv[i]%mod;
}
inline ll C(int n,int m){return fac[n]*inv[m]%mod*inv[n-m]%mod;}
int main(){
	scanf("%d%d",&n,&k),precalc(n+k),f[0][0]=1;
	for(int i=1;i<=450;++i)
		for(int j=i;j<=k;++j){
			f[i][j]=(f[i][j-i]+f[i-1][j-i])%mod;
			if(j>n)f[i][j]=(f[i][j]-f[i-1][j-n-1]+mod)%mod;
		}
	for(int i=0;i<=k;++i){
		int coef=0;
		for(int j=0;j<=450;++j){
			if(j&1)(coef+=mod-f[j][i])%=mod;
			else (coef+=f[j][i])%=mod;
		}
		(ans+=C(k-i+n-1,n-1)*coef%mod)%=mod;
	}
	return printf("%d\n",ans),0;
}
posted @ 2022-07-23 19:13  Samsara-soul  阅读(104)  评论(0编辑  收藏  举报