Bzoj 2839 集合计数 题解
2839: 集合计数
Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 495 Solved: 271
[Submit][Status][Discuss]
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;
一开始想到的状态数组是f[i],代表我们取交集共选了i个数,但是转移还要去枚举有几个集合,好像挺不靠谱的……
一看正解,连状态数组都不用,让我想到了10.9考试 第一题建造城市的打法。
我们同样,先从n个数里提前选出k个数,然后去枚举多出来的集合的交集至少是多少,仍然奇减偶加,假设我们当前要求的是交集多出至少为x的方案数,那么就是:
(2^(2^(n-k-x))-1)*C(n-k,x)。
在(2^(2^(n-k-x))-1)即表示在除去k+x个数后的2^(n-k-x)集合中选集合的方案数,由于我们不能一个都不选,所以还得减去全部不选的情况。C(n-k,x)就是在剩下n-k个数中选出x个数的方案数,最后在将总和乘以在n个数中选k个数的方案数。
1 #include <iostream> 2 #include <cstdlib> 3 #include <cstdio> 4 #include <cstring> 5 #include <queue> 6 #include <algorithm> 7 #include <cmath> 8 #include <map> 9 #define N 1000005 10 using namespace std; 11 int n,k,p=1000000007; 12 long long jc[N],ni[N],xp[N]; 13 long long ksm(long long x,long long z) 14 { 15 long long ans=1; 16 while(z>0) 17 { 18 if(z&1) 19 { 20 ans*=x; 21 ans%=p; 22 } 23 x*=x;x%=p; 24 z>>=1; 25 } 26 return ans; 27 } 28 int main() 29 { 30 scanf("%d%d",&n,&k); 31 jc[0]=1;xp[0]=1; 32 for(int i=1;i<=n;i++) 33 { 34 jc[i]=(jc[i-1]*i)%p; 35 xp[i]=(xp[i-1]*2)%p; 36 } 37 ni[n]=ksm(jc[n],p-2); 38 for(int i=n-1;i>=1;i--)ni[i]=(ni[i+1]*(i+1))%p; 39 ni[0]=1;long long now=2; 40 long long ans=0; 41 for(int i=n-k;i>=0;i--) 42 { 43 long long tmp=((((now-1)*jc[n-k]%p)*ni[i]%p)*ni[n-k-i])%p; 44 if(i&1)ans=(ans-tmp+p)%p; 45 else 46 { 47 ans+=tmp; 48 ans%=p; 49 } 50 now*=now; 51 now%=p; 52 } 53 ans*=((jc[n]*ni[k])%p*ni[n-k])%p; 54 ans%=p; 55 printf("%lld\n",ans); 56 return 0; 57 }