bzoj 2839 : 集合计数 容斥原理
因为要在n个里面选k个,所以我们先枚举选的是哪$k$个,方案数为$C_{n}^k$
确定选哪k个之后就需要算出集合交集正为好这$k$个的方案数,考虑用容斥原理。
我们还剩下$n-k$个元素,交集至少为$k$的方案数为$2^{2^{n-k}}$。
相当于在仅有剩下$n-k$个元素的集合里随便选,最后再往每个集合里塞进这$k$个元素。
然后就是很简单的容斥了。
减去交集至少为k加上其他一个元素的方案数,加上交集至少为k加上其他两个元素的方案数。。。
$$ans=C_{n}^k\times(2^{2^{n-k}}-C_{n-k}^1\times 2^{2^{n-k-1}}+C_{n-k}^2\times 2^{2^{n-k-2}}-.....)$$
好像网上其他做法跟我不太一样呢。。。
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #define N 1000005 6 #define ll long long 7 using namespace std; 8 const int p = 1000000007; 9 int n,k; 10 ll pw(int x,int y) 11 { 12 ll lst=1; 13 while(y) 14 { 15 if(y&1)lst=lst*x%p; 16 y>>=1; 17 x=(ll)x*x%p; 18 } 19 return lst; 20 } 21 int pow[N],jie[N]; 22 int main() 23 { 24 pow[0]=1;jie[0]=1; 25 scanf("%d%d",&n,&k); 26 for(int i=1;i<=n-k;i++)pow[i]=(pow[i-1]*2)%(p-1); 27 for(int i=1;i<=n;i++)jie[i]=(1LL*jie[i-1]*i)%p; 28 ll ans=0; 29 ans=pw(2,pow[n-k]); 30 for(int i=1;i<=n-k;i++) 31 { 32 int tmp=1LL*pw(2,pow[n-k-i])*jie[n-k]%p*pw(jie[i],p-2)%p*pw(jie[n-k-i],p-2)%p; 33 if(i&1)ans=(ans-tmp+p)%p; 34 else ans=(ans+tmp)%p; 35 } 36 ans=ans*jie[n]%p*pw(jie[k],p-2)%p*pw(jie[n-k],p-2)%p; 37 printf("%lld\n",ans); 38 return 0; 39 }