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})\)。
#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;
}