[BZOJ2839]集合计数
Description
一个有N个元素的集合有2^N个不同子集(包含空集),现在要在这2^N个集合中取出若干集合(至少一个),使得
它们的交集的元素个数为K,求取法的方案数,答案模1000000007。(是质数喔~)
Input
一行两个整数N,K
Output
一行为答案。
Sample Input
3 2
Sample Output
6
首先我们选出$k$个元素作为交集,方案数为$\binom{n}{k}$,那么接下来不管怎么选择集合的交集一定为空
根据容斥原理:$\varnothing=$总方案数$-$至少有一个交集的方案数$+$至少有两个交集的方案数$+\cdots+(-1)^{n}$至少有$n$个交集的方案数
设$f_i$表示至少有$i$个交集的方案数,那么$f_i=\binom{n}{i}(2^{2^{n-i}}-1)$
这里解释一下这个式子的意义:首先任选$i$个数作为交集,然后剩下$n-i$个数可选可不选,但不能全部不选,一共能够成$2^{n-i}$种集合,然后我们从这些集合中任选若干个,这里是考虑每个集合选还是不选,方案数为$\binom{n}{i}(2^{2^{n-i}}-1)$
然后根据容斥定理,$ans=\binom{n}{k}\sum_{i=0}^{n-k}(-1)^if_i$
代码:
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #define mod 1000000007 5 #define M 1000010 6 #define int long long 7 using namespace std; 8 int n,k,ans,now=2; 9 int h[M],inv[M]; 10 int power(int a,int b) { 11 int ans=1; 12 while(b) { 13 if(b&1) ans=1ll*ans*a%mod; 14 b>>=1,a=1ll*a*a%mod; 15 }return ans; 16 } 17 int C(int n,int m) { 18 return 1ll*h[n]*inv[m]%mod*inv[n-m]%mod; 19 } 20 #undef int 21 int main() { 22 #define int long long 23 scanf("%lld%lld",&n,&k); 24 h[0]=1;inv[0]=1; 25 for(int i=1;i<=n;i++) 26 h[i]=h[i-1]*i%mod,inv[i]=power(h[i],mod-2); 27 n-=k; 28 for(int i=n;i>=0;i--) { 29 (ans+=((i&1)?-1:1)*1ll*C(n,i)*(now-1)%mod)%=mod; 30 now=1ll*now*now%mod; 31 if(ans<0) ans+=mod; 32 } 33 ans=1ll*ans*C(n+k,k)%mod; 34 printf("%lld\n",ans); 35 return 0; 36 }