传送门

题意

n,k,求出长度为n的逆序对数恰好为k的排列的个数。

思路

考虑从小到大插入i(偏序类题目常用方法:定序)。对逆序对数的贡献必为[0,i1]
还有一个贡献和为k的限制。考虑生成函数
i=1n1xi1x

=i=1n(1xi)(1x)n

=(i=1n(1xi))(j=0(j+n1j)xj)

因此枚举j找到[xkj](i=1n(1xi))累计起来即可。
i=1n(1xi)互异分拆的生成函数。

互异的数的数量2n
因此用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;
}