[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 }

 

posted @ 2018-12-06 18:29  Slr  阅读(309)  评论(1编辑  收藏  举报