容斥原理是一种计数方法。
这种方法的基本思想是:先不考虑重叠的情况,把包含于某内容中的所有对象的数目先计算出来,然后再把计数时重复计算的数目排斥出去,使得计算的结果既无遗漏又无重复
举个例子
1.两个集合的情况
A,B是两个集合
所以连个集合的并:|A + B| = |A| + |B| - |AB|
2.三个集合的情况
三个集合的并:|A + B + C| = |A| + |B| + |C| - |AB| - |AC| - |BC| + |ABC|
可以看出两种情况都减去了重复的部分。
可以得到如下等式:
所以我们怎么知道什么时候改减什么时候该加。
看元素个数,或者说集合个数,奇加偶减。
模板代码:
LL slove(LL N)
{
LL res=0;
for(LL i=1;i<(1<<k);i++)
{
LL num=0;
for(LL j=i;j!=0;j>>=1) num+=j&1; //i的二进制表示中1的个数
LL lcm=1;
for(LL j=0;j<k;j++)
{
if(i>>j&1)
{
lcm=lcm/gcd(lcm,m[j])*m[j]; //求最小公倍数
if(lcm>N) break; //如果lcm>n,则n/lcm=0,因此在益处之前跳出
}
}
if(num%2==0) res-=N/lcm; //偶减
else res+=N/lcm; //奇加
}
return res;
}
另一种写法:
ans = fac[n];
flag = -1;//容斥的符号变化
for(int i = 1; i <= n; i ++){
ans += flag * fac[n] / fac[i];
flag = -flag;
}