题意
给\(n\),\(k\),求出长度为\(n\)的逆序对数恰好为\(k\)的排列的个数。
思路
考虑从小到大插入\(i\)(偏序类题目常用方法:定序)。对逆序对数的贡献必为\([0,i-1]\)。
还有一个贡献和为\(k\)的限制。考虑生成函数。
\(\prod\limits_{i=1}^n \dfrac{1-x^i}{1-x}\)
\(=\dfrac{\prod\limits_{i=1}^n(1-x^i)}{(1-x)^n}\)
\(=(\prod\limits_{i=1}^n(1-x^i))*(\sum\limits_{j=0}\binom{j+n-1}{j}x^j)\)
因此枚举\(j\)找到\([x^{k-j}](\prod\limits_{i=1}^n(1-x^i))\)累计起来即可。
而\(\prod\limits_{i=1}^n(1-x^i)\)是互异分拆的生成函数。
互异的数的数量\(\le 2\sqrt{n}\)
因此用dp+根号分治的方法,\(f[i][j]\)表示选择\(i\)个和为\(j\)的方案数。
这里有一个我没有搞懂的地方:为什么\(j>n\)时,要去把\(n+1\)算进去的重,为什么不去掉\(n+2,n+3...\)之类的呢。我还会再思考一下的……
code
点击查看代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+5;
const int mod=1e9+7;
ll ksm(ll a,ll b) {ll mul=1;for(;b;b>>=1,a=a*a%mod)if(b&1)mul=mul*a%mod;return mul;}
int n,k,S;
ll jc[N],ijc[N],f[2][N],g[N];
int _pd(int x) {return (x&1)?-1:1;}
void _pre() {
S=(int)ceil(sqrt(k))*2;
f[0][0]=1;g[0]=1;
for(int i=1;i<=S;i++) {
bool o=i&1,_o=o^1;
for(int j=0;j<i;j++) {f[o][j]=0;}
// memset(f[o],0,sizeof(f[o]));
for(int j=i;j<=k;j++) {
f[o][j]=(f[o][j-i]+f[_o][j-i])%mod;
if(j>n) {f[o][j]=(f[o][j]-f[_o][j-n-1])%mod;}
g[j]=(g[j]+_pd(o)*f[o][j])%mod;
}
}
int up=n+k;
jc[0]=1;for(int i=1;i<=up;i++)jc[i]=jc[i-1]*i%mod;
ijc[up]=ksm(jc[up],mod-2);for(int i=up;i;i--)ijc[i-1]=ijc[i]*i%mod;
}
ll binom(int a,int b) {return jc[a]*ijc[b]%mod*ijc[a-b]%mod;}
void solve() {
ll ans=0;
for(int j=0;j<=k;j++) {
ans=(ans+binom(j+n-1,j)*g[k-j])%mod;
}
printf("%lld",(ans+mod)%mod);
}
int main() {
scanf("%d%d",&n,&k);
_pre();
solve();
return 0;
}