二项式反演笔记

二项式反演

\[f(n)=\sum^{n}_{i=0} g(i) C_{n}^{i} \]

\[g(n)=\sum^{n}_{i=0}(-1)^{n-i}f(i)C_{n}^{i} \]

证明:

代入\(g(i)\)得:

\[g(n)=\sum^{n}_{i=0}(-1)^{n-i}C_{n}^{i}\sum_{j=0}^{i}g(j)C_{i}^{j} \]

改变枚举顺序可得:

\[g(n)=\sum^{n}_{i=0}(-1)^{n-i}C_{n}^{i}C_{i}^{j}\sum_{j=0}^{i}g(j) \]

\[g(n)=\sum_{j=0}^{n}g(j)\sum^{n}_{i=0}(-1)^{n-i}C_{n}^{i}C_{i}^{j} \]

我们可以通过暴力得到一个式子:

\[C_{n}^{i}C_{i}^{j}= \frac{n!}{i!(n-i)!}\times\frac{i!}{j!(i-j)!}=\frac{n!}{j!(n-j)!}\times\frac{(n-j)!}{(n-i)!(i-j)!}=C^{j}_{n}C^{n-i}_{n-j} \]

把这个式子代入得:

\[g(n)=\sum_{j=0}^{n}g(j)\sum^{n}_{i=0}(-1)^{n-i}C^{j}_{n}C^{n-i}_{n-j} \]

所以

\[g(n)=\sum_{j=0}^{n}g(j)C^{j}_{n}\sum^{n}_{i=0}(-1)^{n-i}C^{n-i}_{n-j} \]

\(i\ne n\)时,由二项式定理得:

\[\sum^{n}_{i=0}(-1)^{n-i}C^{n-i}_{n-j}=(-1+1)^{n-j}=0 \]

所以(此时\(i= n\)):

\[g(n)=g(n)C^{n}_{n}=g(n) \]

证毕

例题

洛谷P4859 已经没有什么好害怕的了

由题意得,\(k=\frac{n+k}{2}\) (和差问题)

我们首先先将\(a,b\)从小到大排序

\(f[i][j]\)表示前\(i\)\(a\)中,选了\(j\)\(a\)使得\(a>b\) 得方案数

\(last[i]\)表示\(b\)中最后一个\(<a[i]\)的数的编号

无非就是选和不选,于是我们很容易得出状态转移方程:

\[f[i][j]=f[i-1][j]+(last[i]-j+1)*f[i-1][j-1] \]

这里我们遇见了一个二项式反演题目中得一个常见套路:至少恰好

\(g[i]\)表示至少选了\(i\)个的方案数,\(ans[i]\)表示恰好选了\(i\)的方案数(就是我们要的答案)

在先前的dp中,我们还剩下一些没有选的数,它们可以任意排列

于是:

\[g[i]=f[n][i]*(n-i)! \]

对于每个\(g[i]\),我们试图寻找它和所有比\(i\)大的\(j\)\(ans[j]\)的关系

对于每个\(ans[j]\)中的每一种方案,我们可以从所有\(j\)个人中任意选择若干个人

所以我们得到:

\[g[i]=\sum_{j=i}^{n}C_{j}^{i}ans[j] \]

由二项式反演得:

\[ans[j]=\sum_{j=i}^{n}(-1)^{j-i}C^{i}_{j}g[j] \]

代码被我Gu掉了)

#include<bits/stdc++.h>
#define int long long
#define MOD 1000000009
using namespace std;
const int maxn=2e3+5;
int n,k;
int a[maxn];
int b[maxn];
int dp[maxn][maxn];
int last[maxn];
int f[maxn];
int invf[maxn];
int g[maxn];
int ans[maxn];
inline int ksm(int x,int y){
	if(!y) return 1;
	if(y==1) return x%MOD;
	int tmp=ksm(x,y/2)%MOD;
	tmp=(1ll*tmp*tmp)%MOD;
	if(y&1) return (tmp*x)%MOD;
	else return tmp;
}
inline int calc(int x,int y){
	return 1ll*f[x]*invf[y]%MOD*invf[x-y]%MOD;
}
signed main(){
	scanf("%lld%lld",&n,&k);
	for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
	for(int i=1;i<=n;i++) scanf("%lld",&b[i]);
	if((n+k)&1){
		printf("0\n");
		return 0;
	}
	sort(a+1,a+1+n);
	sort(b+1,b+1+n);
	f[0]=1;
	invf[0]=1;
	for(int i=1;i<=n;i++) f[i]=(1ll*f[i-1]*i)%MOD;
	invf[n]=ksm(f[n],MOD-2);
	for(int i=n-1;i>=1;i--) invf[i]=(1ll*invf[i+1]*(i+1))%MOD;
	k=(k+n)/2;
	for(int i=1;i<=n;i++) last[i]=lower_bound(b+1,b+1+n,a[i])-b-1;
	dp[0][0]=1;
	for(int i=1;i<=n;i++){
		dp[i][0]=dp[i-1][0];
		for(int j=1;j<=i;j++)
			dp[i][j]=(1ll*dp[i][j]+(dp[i-1][j]+(1ll*dp[i-1][j-1]*max(0ll,last[i]-j+1))%MOD)%MOD)%MOD;
	}
	for(int i=1;i<=n;i++)	
		g[i]=(1ll*dp[n][i]*f[n-i])%MOD;
	int ans=0;
	for(int i=k;i<=n;i++)	
		if((i-k)&1) ans-=(1ll*calc(i,k)*g[i])%MOD,ans%=MOD;
		else ans+=(1ll*calc(i,k)*g[i])%MOD,ans%=MOD;
	while(ans<0) ans+=MOD;
	printf("%lld\n",ans);
	return 0;
}
posted @ 2019-06-02 16:38  ybwowen  阅读(230)  评论(0编辑  收藏  举报