传送门

题意

\(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;
}