【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; }
| 欢迎来原网站坐坐! >原文链接<