bzoj2839 集合計數 組合計數 容斥原理|題解

集合計數

題目描述

一個有N個元素的集合有2^N個不一樣子集(包含空集),如今要在這2^N個集合中取出若干集合(至少一個),使得它們的交集的元素個數爲K,求取法的方案數,答案模1000000007。(是質數喔~)ios

輸入格式

一行兩個整數N,K算法

輸出格式

一行爲答案。ide

樣例

樣例輸入

3 2

樣例輸出

6

數據範圍與提示

樣例說明

假設原集合爲{A,B,C}ui

則知足條件的方案爲:{AB,ABC},{AC,ABC},{BC,ABC},{AB},{AC},{BC}spa

數聽說明

對於100%的數據,1≤N≤1000000;0≤K≤N;code

 

 

 

題解blog

看到這個題咱們很天然的想到答案是it

$\binom{n}{k}*f(n-k)$io

其中f(i)表示i個元素的2i個集合中,選出任意多集合使交集爲空的方案數,可是一個集合都不選是不合法的event

 

一個暴力算法

顯然f(0)=1

設g(i,j)表示從i個元素的集合中,選出任意多集合使交集爲k個的方案數

$g(i,j)=\binom{i}{j}*f(i-j)$

對於i>1    $f(i)=2^{2^i}-1-\sum\limits_{j=1}^{i}g(i,j)$

注意不能一個集合都不選,但能夠選擇集合中沒有任何元素的集合來組成一個對集合的集合,這涉及到-1的位置

複雜度O(n2)  指望得分70

 

正解 容斥原理

 

$f(n)=\sum\limits_{i=0}^{n}*(-1)^i*\binom{n}{i}*(2^{2^{n-i}}-1)$

集合A B C表示交集中含有 a,b,c的集合取法

C(n,i)表示從n個形如A B C的集合中取出i個,算出有多少種取法

這i個集合的交集則表示同時含有這i個元素

後一項則表示其餘集合任意選取,但不能一個都不選的方案數

偶加奇減,則獲得全集減去這幾個集合的並集,獲得f(i)

 1 #include<iostream>
 2 #include<cstdio>
 3 #define ll long long
 4 using namespace std;  5 const int mod=1e9+7;  6 int n,k;  7 ll js[1000010],jsinv[1000010];  8 ll qpow(ll base,int y,int mo)  9 { 10     ll ans=1; 11     while(y) 12  { 13         if(y&1) ans=ans*base%mo; 14         base=base*base%mo; 15         y>>=1; 16  } 17     return ans; 18 } 19 void init() 20 { 21     js[0]=1; 22     for(int i=1;i<=n;i++) js[i]=js[i-1]*i%mod; 23     jsinv[n]=qpow(js[n],mod-2,mod); 24     for(int i=n-1;i>=0;i--) jsinv[i]=jsinv[i+1]*(i+1)%mod; 25 } 26 inline ll C(int n,int m) 27 { 28     return js[n]*jsinv[m]%mod*jsinv[n-m]%mod; 29 } 30 inline ll ask(int m) 31 { 32     ll ans=0; 33     for(int i=0,u=1;i<=m;i++,u=-u) 34         ans=(ans+u*C(m,i)*(qpow(2,qpow(2,m-i,mod-1),mod)-1)%mod)%mod; 35     return ans; 36 } 37 int main() 38 { 39     scanf("%d%d",&n,&k); 40  init(); 41     printf("%lld\n",(ask(n-k)*C(n,k)%mod+mod)%mod); 42     return 0; 43 }
View Code

 

 

 

另外一種等價的方法

$ans=\binom{n}{k}*\sum\limits_{i=k}^{n}(-1)^{i-k}*\binom{n-k}{i-k}*(2^{2^{n-i}}-1)$

這種方法能夠理解爲固定一種組合,從其餘集合中選取幾個進行容斥

也能算出答案

 1 #include<iostream>
 2 #include<cstdio>
 3 #define ll long long
 4 using namespace std;  5 const int mod=1e9+7;  6 int n,k;  7 ll js[1000010],jsinv[1000010];  8 ll qpow(ll base,int y,int mo)  9 { 10     ll ans=1; 11     while(y) 12  { 13         if(y&1) ans=ans*base%mo; 14         base=base*base%mo; 15         y>>=1; 16  } 17     return ans; 18 } 19 void init() 20 { 21     js[0]=1; 22     for(int i=1;i<=n;i++) js[i]=js[i-1]*i%mod; 23     jsinv[n]=qpow(js[n],mod-2,mod); 24     for(int i=n-1;i>=0;i--) jsinv[i]=jsinv[i+1]*(i+1)%mod; 25 } 26 inline ll C(int n,int m) 27 { 28     return js[n]*jsinv[m]%mod*jsinv[n-m]%mod; 29 } 30 int main() 31 { 32     scanf("%d%d",&n,&k); 33  init(); 34     ll ans=0; 35     for(int i=k,u=1;i<=n;i++,u=-u) 36         ans=(ans+u*C(n-k,i-k)%mod*(qpow(2,qpow(2,n-i,mod-1),mod)-1)%mod)%mod; 37     printf("%lld\n",(ans*C(n,k)%mod+mod)%mod); 38     return 0; 39 }
View Code
相關文章
相關標籤/搜索