P4859 已经没有什么好害怕的了(二项式反演)

题目

思路

显然是恰好有(frac{n+k}{2})(a>b)

(f(i,j))表示前(i)个糖果,已经有(j)(a>b),剩下的没管的方案数

(a)数组从小到大排序,设(r_i)表示比(a_i)小的(b)个数,那么(r_i)是递增的

有状态转移方程(f(i,j) = f(i-1,j) + f(i-1,j-1) imes (r_i-j+1))

对于每个(f(n,i)),由于剩下的(n-i)对不知道大小情况,那么有(f(n,i) imes (n-i)!)种情况至少(i)个匹配

(g_i)表示恰好有(i)个匹配,那么(f(n,i) imes (n-i)! = Sigma_{j=i}^n C(j,i) imes g_i)

二项式反演后就是(g_i = Sigma_{j=i}^n (-1)^{j-i}C(j,i) imes f(n,j) imes (n-j)!)

#include<bits/stdc++.h>
#define N 2005 
using namespace std;
typedef long long ll;
const ll mod = 1000000009;
int n,k,a[N],b[N],r[N];
ll f[N][N];
ll fac[N],ifac[N];

template <class T> inline T Max(T a,T b) { return a > b ? a : b; }
template <class T> inline T Min(T a,T b) { return a < b ? a : b; }
template <class T> void read(T &x)
{
	char c;int sign=1;
	while((c=getchar())>'9'||c<'0') if(c=='-') sign=-1; x=c-48;
	while((c=getchar())>='0'&&c<='9') x=(x<<1)+(x<<3)+c-48; x*=sign;
}
ll qp(ll a,ll b) { ll ret=1; for(;b;b>>=1,a=a*a%mod) if(b&1) ret=ret*a%mod; return ret; }
ll C(int n,int m) { return n>=m ? fac[n]*ifac[m]%mod*ifac[n-m]%mod : 0; }
void init(int maxn)
{
	fac[0]=1; for(int i=1;i<=maxn;++i) fac[i]=fac[i-1]*i%mod;
	ifac[maxn]=qp(fac[maxn],mod-2);
	for(int i=maxn-1;i>=0;--i) ifac[i]=ifac[i+1]*(i+1)%mod; 
}
int main()
{
	read(n);read(k);
	for(int i=1;i<=n;++i) read(a[i]);
	for(int i=1;i<=n;++i) read(b[i]);
	sort(a+1,a+n+1); sort(b+1,b+n+1);
	if((n+k)&1) { printf("0"); return 0; }
	for(int i=1;i<=n;++i)
	  for(int j=1;j<=n;++j)
	    r[i]+=(a[i]>b[j]);
	init(2000);
	f[0][0]=1;
	for(int i=1;i<=n;++i)
	{
		f[i][0]=f[i-1][0];
		for(int j=1;j<=i;++j) f[i][j] = (f[i-1][j] + f[i-1][j-1] * max(0,r[i]-j+1))%mod;
	}
	ll ans=0;
	k=(n+k)/2;
	for(int i=k;i<=n;++i) ans = (ans + ((i-k)&1 ? -1 : 1) * C(i,k) * f[n][i]%mod *fac[n-i]%mod)%mod;
	printf("%lld
",(ans%mod+mod)%mod);
	return 0;
}
原文地址:https://www.cnblogs.com/Chtholly/p/11967407.html