【BZOJ2839】集合计数 组合数+容斥

【BZOJ2839】集合计数

Description

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

Input

一行两个整数N,K

Output

一行为答案。

Sample Input

3 2

Sample Output

6

HINT

【样例说明】
假设原集合为{A,B,C}
则满足条件的方案为:{AB,ABC},{AC,ABC},{BC,ABC},{AB},{AC},{BC}
【数据说明】
     对于100%的数据,1≤N≤1000000;0≤K≤N;

题解:容斥,考虑选出若干集合使得交集至少为k的方案数,有$f(i)=C _n^i \times (2^{2^{n-i}}-1)$,可以理解为已经选定了i个,剩下$2^{n-i}$个集合,每个可以选或不选,但是不能一个也不选。但是这样做肯定会有重复的,我们思考容斥系数是什么。

当我们计算交集至少为k的时候,每个交集为j的方案都会被计算$C_j^k$次,所以
f(k)的系数是1
f(k+1)的系数是$-C_{k+1}^k$
f(k+2)的系数$-C_{k+2}^k+C_{k+1}^kC_{k+2}^{k+1}=C_{k+2}^k$(小tips:$C_N^MC_M^S=C_N^SC_{N-S}^{N-M}$)

以此类推,f(i)的系数就是$(-1)^{i-k}C_i^k$。

所以答案为$\sum\limits_{i=k}^n(-1)^{i-k}C_i^kC_n^i(2^{2^{n-i}}-1)$

求组合数需要线性筛逆元,方法:$i^{-1}\equiv -\lfloor{p\over i}\rfloor\times(p\%i)^{-1}(\mod p)$

求$(2^{2^i}-1)$可以采用从n到k枚举i的方法,初值tmp=1,然后tmp=tmp*(tmp+2)。

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
typedef long long ll;
const ll mod=1000000007;
ll n,k,ans;
ll ine[1000010],jcc[1000010],jc[1000010];
ll c(ll x,ll y)
{
	return jc[x]*jcc[y]%mod*jcc[x-y]%mod;
}
int main()
{
	scanf("%lld%lld",&n,&k);
	ll i,j,flag,tmp;
	ine[1]=jc[1]=jcc[1]=jc[0]=jcc[0]=1;
	for(i=2;i<=n;i++)
	{
		ine[i]=(mod-(mod/i)*ine[mod%i])%mod;
		jcc[i]=jcc[i-1]*ine[i]%mod;
		jc[i]=jc[i-1]*i%mod;
	}
	for(i=n,flag=((n-k)&1)?-1:1,tmp=1;i>=k;i--)
	{
		ans=(ans+mod+flag*c(i,k)*c(n,i)%mod*tmp%mod)%mod;
		flag=-flag,tmp=tmp*(tmp+2)%mod;
	}
	printf("%lld",ans);
	return 0;
}
posted @ 2017-07-06 08:16  CQzhangyu  阅读(1202)  评论(1编辑  收藏  举报