bzoj2839 集合计数 容斥

28 篇文章 0 订阅
1 篇文章 0 订阅

Description


一个有N个元素的集合有2N个不同子集(包含空集),现在要在这2N个集合中取出若干集合(至少一个),使得
它们的交集的元素个数为K,求取法的方案数,答案模1000000007。(是质数喔~)

Solution


这个容斥就很简单了
考虑设g[k]表示至少k个相同的答案,f[k]表示恰好k个相同的答案
显然有 g k = ∑ i = k n ( i k ) f k g_k=\sum\limits_{i=k}^{n}{\binom{i}{k}f_k} gk=i=kn(ki)fk
考虑g[]的含义,不难得到 g k = ( n k ) ⋅ ( 2 2 n − k − 1 ) g_k=\binom{n}{k}\cdot\left(2^{2^{n-k}}-1\right) gk=(kn)(22nk1)
然后有一个神奇的变换就是 f k = ∑ i = k n ( − 1 ) i − k ( i k ) g k f_k=\sum\limits_{i=k}^n{{(-1)}^{i-k}\binom{i}{k}g_k} fk=i=kn(1)ik(ki)gk
这是一个反演,证明的话带入一下就可以了。。
于是我们就可以 O ( n ) 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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值