VEXOBEN
Vexoben
Jul 11, 2018
It takes 4 minutes to read this article.

题目链接:https://www.lydsy.com/JudgeOnline/problem.php?id=3622

(刷新以获取数学公式)

题意

给定长度为n(<=100000)的数组,将其两两配对,使的对数比的对数多对,求方案数。

保证中的数两两不同。

题解

显然只需要的对数为即可。

首先将数组排序。

表示在的前个数中选出个数,在中随意选个数,且这对数全部满足的方案数。

中小于的数的个数,那么我们得到转移方程:

求出来的显然不是答案。因为我们只考虑到了的那些怎么选择与排布,却没管那些的配对。

为配对所有的个数,恰好有对满足了

下面考虑将相互表示。

中没有配对的个数随意匹配,得到种方案。要注意这些随意匹配之后也有可能出现的匹配。

考虑,对于,从的匹配里任选组都会对算入的贡献中。

于是我们得到:

这个似乎可以直接反演。暴力点的话爆算就好了

#include<bits/stdc++.h>
using namespace std;
const int N=2e3+10;
const int mod=1e9+9;
 
int n,k,a[N],b[N],fac[N];
int f[N],g[N],fx[N][N],c[N][N];
 
int main() {
	scanf("%d%d",&n,&k);
	for (int i=1;i<=n;i++) scanf("%d",&a[i]);
	for (int i=1;i<=n;i++) scanf("%d",&b[i]);
	if ((n+k)%2==1) return 0*puts("0");
	sort(a+1,a+n+1); sort(b+1,b+n+1);
	for (int i=1;i<=n;i++) a[i]=upper_bound(b+1,b+n+1,a[i])-b-1;
	fac[0]=1;
	for (int i=1;i<=n;i++) fac[i]=1LL*fac[i-1]*i%mod;
	for (int i=0;i<=n;i++) {
		for (int j=0;j<=i;j++) {
			if (j==0) c[i][j]=1;
			else c[i][j]=c[i-1][j]+c[i-1][j-1];
			if (c[i][j]>=mod) c[i][j]-=mod;
		}
	}
	fx[0][0]=1;
	for (int i=1;i<=n;i++) {
		for (int j=0;j<=i;j++) {
			fx[i][j]=fx[i-1][j];
			if (j>0) fx[i][j]+=1LL*fx[i-1][j-1]*(a[i]-j+1)%mod;
			if (fx[i][j]>=mod) fx[i][j]-=mod;
		}
	}
	for (int i=0;i<=n;i++) f[i]=fx[n][i];
	for (int i=n;i>=1;i--) {
		g[i]=1LL*f[i]*fac[n-i]%mod;
		for (int j=i+1;j<=n;j++) {
			g[i]=(g[i]-1LL*c[j][i]*g[j])%mod;
			if (g[i]<0) g[i]+=mod;
		}
	}
	printf("%d\n",g[(n+k)/2]);
	return 0;
}