bzoj2839 集合計數 容斥

Description


一個有N個元素的集合有2N個不同子集(包含空集),現在要在這2N個集合中取出若干集合(至少一個),使得
它們的交集的元素個數爲K,求取法的方案數,答案模1000000007。(是質數喔~)

Solution


這個容斥就很簡單了
考慮設g[k]表示至少k個相同的答案,f[k]表示恰好k個相同的答案
顯然有gk=i=kn(ik)fkg_k=\sum\limits_{i=k}^{n}{\binom{i}{k}f_k}
考慮g[]的含義,不難得到gk=(nk)(22nk1)g_k=\binom{n}{k}\cdot\left(2^{2^{n-k}}-1\right)
然後有一個神奇的變換就是fk=i=kn(1)ik(ik)gkf_k=\sum\limits_{i=k}^n{{(-1)}^{i-k}\binom{i}{k}g_k}
這是一個反演,證明的話帶入一下就可以了。。
於是我們就可以O(n)O(n)求恰好k的答案了

沒有交,本地拍了一下似乎沒毛病

Code


#include <stdio.h>
#include <string.h>
#include <algorithm>
#define rep(i,st,ed) for (int i=st;i<=ed;++i)

typedef long long LL;
const int MOD=1000000007;
const int N=200005;

LL fac[N],inv[N],f[N],g[N];

LL C(int n,int m) {
	return fac[n]*inv[m]%MOD*inv[n-m]%MOD;
}

LL ksm(LL x,LL dep,int MOD) {
	LL res=1;
	for (;dep;dep>>=1) {
		(dep&1)?(res=res*x%MOD):0;
		x=x*x%MOD;
	}
	return res;
}

int main(void) {
	freopen("data.in","r",stdin);
	freopen("myp.out","w",stdout);
	fac[0]=fac[1]=1; rep(i,2,N-1) fac[i]=fac[i-1]*i%MOD;
	inv[0]=inv[1]=1; rep(i,2,N-1) inv[i]=(MOD-MOD/i)*inv[MOD%i]%MOD;
	rep(i,2,N-1) inv[i]=inv[i-1]*inv[i]%MOD;
	int n,k; scanf("%d%d",&n,&k);
	rep(i,1,n) g[i]=C(n,i)*(ksm(2,ksm(2,n-i,MOD-1),MOD)-1)%MOD;
	rep(j,k,n) {
		LL tmp=C(j,k)*g[j]%MOD;
		if ((j-k)&1) f[k]=(f[k]+MOD-tmp)%MOD;
		else f[k]=(f[k]+tmp)%MOD;
	}
	printf("%lld", f[k]);
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章