1. 程式人生 > >BZOJ2839:集合計數(容斥,組合數學)

BZOJ2839:集合計數(容斥,組合數學)

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;

Solution

首先考慮一下容斥
設$f(k)$表示選出一些集合使它們交集大小至少為$k$的方案數。
那麼$f(k)=C_n^k \times (2^{2^{n-k}}-1)$
這玩意兒怎麼理解呢?也就是先把那$i$個數確定下來,然後有$2^{n-k}$個集合可以包含那$k$個數。這些集合要麼選要麼不選,但不能一個都不選,也就是不能為空集。所以有$2^{2^{n-k}}-1$種選擇方法。
那麼容斥係數呢?可以發現當計算交集至少為$k$的方案時候,交集至少為$j$的方案($j>k$)會被計算$C_j^k$次。
也就是說,
$f(k)$的係數為$1$。
$f(k+1)$的係數為$-C_{k+1}^k$。
$f(k+2)$的係數為$-C_{k+2}^k+C_{k+1}^k\times C_{k+2}^{k+1}=C_{k+2}^k$
為什麼$f(k+2)$能那麼推呢……因為$C_N^M\times C_M^S=C_N^S\times C_{N-S}^{N-M}$
搞到現在基本可以組合計數搞搞出解了,至於那個大的一比的$2^{2^{n-k}}$,根據尤拉定理直接指數取模$φ(MOD)$就好了,顯然$φ(MOD)=MOD-1$。

Code

 1 #include<iostream>
 2 #include<cstdio>
 3 #define N (1000009)
 4 #define LL long long
 5 #define MOD (1000000007)
 6 using namespace std;
 7 
 8 LL n,k,ans,inv[N],fac[N],facinv[N],p[N];
 9 
10 void Init()
11 {
12     inv[1]=fac[0]=facinv[0]=p[0]=1;
13     for (int i=1; i<=n; ++i)
14 { 15 if (i!=1) inv[i]=(MOD-MOD/i)*inv[MOD%i]%MOD; 16 fac[i]=fac[i-1]*i%MOD; facinv[i]=facinv[i-1]*inv[i]%MOD; 17 p[i]=p[i-1]*2%(MOD-1); 18 } 19 } 20 21 LL Qpow(LL a,LL b) 22 { 23 LL ans=1; 24 while (b) 25 { 26 if (b&1) ans=ans*a%MOD; 27 a=a*a%MOD; b>>=1; 28 } 29 return ans; 30 } 31 32 LL C(LL n,LL m) 33 { 34 if (n<m) return 0; 35 return fac[n]*facinv[m]%MOD*facinv[n-m]%MOD; 36 } 37 38 int main() 39 { 40 scanf("%lld%lld",&n,&k); 41 Init(); 42 for (int i=k,j=1; i<=n; ++i,j=-j) 43 ans+=j*C(n,i)*(Qpow(2,p[n-i])-1)%MOD*C(i,k)%MOD; 44 ans=(ans%MOD+MOD)%MOD; 45 printf("%lld\n",ans); 46 }