[组合数学][容斥]集合计数

首先我们知考虑只有一种交集

交集的大小为K,那么有2N-K个子集包含了这K个元素(确定K个元素,再从剩下N-K个元素任意选取,有2N-K种组合)

用这2N-K个子集能有2^(2^(N-K))种组合,再减去1个空集

一共有CKN种交集,这样得出tmp(K)=CKN *(2^(2^(N-K)-1)种方法

这里面包含了交集大小大于K的取法,所以还要进行容斥

对于交集大小为L,有tmp(L)=CLN *(2^(2^(N-L)-1)种取法

但是tmp(K)多的不止一倍的tmp(L),因为对于每一种交集都多加了tmp(L)

多出CKL 

例如:

  集合为{A,B,C,D,E},K=2

  tmp(K)包含了交集大小为K,K+1,K+2...N的取法种数

  合法的交集是AB,AC,AD...

  但是出现了像BCDE这样不合法的交集

  只计算交集为BC时多余BCDE交集,算BD,BE...同样多出

  所以BCDE被多算了C24

容斥的话,L-N是奇数就减去CKL  *tmp(L),L-N是偶数就加上CKL  *tmp(L)

 1 #include<iostream>
 2 #include<cstring>
 3 #include<cstdio>
 4 using namespace std;
 5 const long long p=(int)1e9+7;
 6 const int MAXN=(int)1e6+100;
 7 int N,K;
 8 long long fac[MAXN],faci[MAXN];
 9 long long qp[MAXN];   //2^2^i
10 long long ans=0;
11 long long qpow(long long a,long long b,long long p){
12     if(a==0)
13         return 0;
14     a%=p;
15     long long out=1;
16     while(b){
17         if(b&1)
18              out=(out*a)%p;
19         b>>=1;
20         a=(a*a)%p;
21     }
22     return out;
23 }
24 
25 long long C(int n,int m){
26     if(m>n)
27         return 0;
28     return ((fac[n]*faci[m]%p)*faci[n-m])%p;
29 }
30 
31 long long T(int i){    //2^(2^(N-i))
32     return qp[N-i]-1;
33 }
34 
35 int main(){
36     scanf("%d%d",&N,&K);
37     qp[0]=2;
38     qp[1]=4;
39     for(int i=2;i<=N;++i)
40         qp[i]=qp[i-1]*qp[i-1]%p;
41     fac[0]=faci[0]=1;
42     for(int i=1;i<=N;++i)
43         fac[i]=(fac[i-1]*i)%p;
44     faci[N]=qpow(fac[N],p-2,p);
45     for(int i=N-1;i>=1;--i)
46         faci[i]=(faci[i+1]*(i+1))%p;
47     ans=C(N,K)*T(K)%p;
48     for(int i=K+1;i<=N;++i){
49         long long fg=( (i-K) & 1 ) ? -1:1;
50         ans+=(fg*C(i,K)*C(N,i)%p)*T(i)%p;
51         ans=(ans+p)%p;
52     }
53     printf("%lld
",ans);
54 }
View Code
原文地址:https://www.cnblogs.com/Duan-Yue/p/11127284.html