(鑑於博客園的表現能力有限,下文中的'^'符合表示集合交,'U'符號表示集合並。'| |'表示集合的元素個數)html
對於最基礎的容斥原理,就是求知足至少一個條件的事物的數量,即:ide
設全集中元素有n個屬性:a1,a2,...,an,有屬性a1的事物的集合爲A1,有屬性a2的事物的集合爲A2,...,有屬性an的事物的集合爲An,求至少有一個屬性的事物的個數,即求|A1UA2U...UAn|spa
有一個公式(公式1):3d
證實以下:code
、htm
還有一個等價公式(公式二): blog
設有 k 種屬性,f(S)爲至少知足屬性集合S的物品個數,同時定義f(∅)=0,則至少有一種屬性的物品的個數爲:get
證實:博客
經過維恩圖得知在 k≤3 的時候,公式是十分清晰易懂的,那麼在 k 更大的狀況下,不妨證實一下爲何這個式子是對的it
考慮每個物品,假設它有 n 個屬性,在 n=0 的時候它對答案的貢獻只會被 f(∅) 計算到,並且對答案的貢獻爲 0。
若是n≥1 ,那麼能夠經過枚舉會計算它的貢獻的集合來計算它的貢獻,那麼它的貢獻爲:
正好爲1,所以這個物品若是至少有一個屬性的話運用這個公式計算代價時必定會只對答案產生1 的貢獻。z
總結:從公式1能夠看出,容斥原理求的就是的複雜的多個集合的並集的大小。常搭配的方法求更復雜的集合的並/交集。
下面再補充一個利用補集求交集、並集的方法。
最後補個代碼實現吧:
(例題 ,感謝YCH巨佬)
1 for(int s1=1;s1<(1<<k);s1++)//只選哪些長度的環 (狀壓) 2 { 3 num1=0;//當前選法選的環數 4 tot=0; 5 lcm=1; 6 for(int i=1;i<=k;++i) 7 if(s1&(1<<i-1)) 8 { 9 num1++; 10 tot+=h[i].tot; 11 lcm=1ll*lcm*h[i].len/gcd(lcm,h[i].len); 12 } 13 tot=qpow(tot,n); 14 for(int s2=(s1-1)&s1;s2;s2=(s2-1)&s1)//容斥過程 15 { 16 v=0; 17 num2=0;//要容斥的選法選的環數 18 for(int i=1;i<=k;++i) 19 { 20 if((1<<i-1)&s2) 21 { 22 num2++; 23 v+=h[i].tot; 24 } 25 } 26 v=qpow(v,n); 27 if((num1&1)==(num2&1)) //判斷容斥係數 28 tot=(tot+v)%mod; 29 else 30 { 31 tot-=v; 32 if(tot<0) 33 tot+=mod; 34 } 35 } 36 ans=(ans+1ll*tot*lcm%mod)%mod; 37 }