(鉴于博客园的表现能力有限,下文中的'^'符合表示集合交,'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 }