容斥原理
想起容斥原理,大家都不陌生。
相信很多地方都会举这样类似一个最简单的例子让大家理解容斥:
现在赛场上有n个人,都参加过WC、CTSC和APIO。
拿过至少一个比赛的金牌的有多少人?
我们可以简单计算拿过WC金牌的人数+拿过CTSC的金牌的人数+拿过APIO的金牌的人数。
但是这样会记重,比如jasonvictoryan,他既拿过WC的金牌又拿过APIO的金牌,又比如czllgzmzl,他既拿过WC的金牌又拿过CTSC的金牌,这样他们都会被计算两次。
于是我们还要减去同时拿过WC和CTSC的金牌的人数+同时拿过WC和APIO的金牌的人数+同时拿过CTSC和APIO的金牌的人数。
但是这样会记少,比如dwjshift,他WC-CTSC-APIO三金。一开始给他记了3次,然后又减去了他3次,那岂不是没有计算到他。
于是最后再加上同时拿过WC、CTSC和APIO的金牌的人数。
推广到更高的情况,也差不多,符号正负交替。
不知你有没有疑问过,为什么这样在更大的情况下是对的呢?
下面我们来讲讲容斥的原理,懂了这个你对容斥的理解和运气都会更上一层,不再是只会套经典容斥模型的人了!
容斥的原理
我们来思考上面容斥的意义。
比如一个人,他拿到了n个赛事的金牌,那么对于拿过金牌的人这个计数问题的容斥式子下,他会被算多少次?
n=0的时候,他自然是不会被计算到,这符合实际。
n>=1呢?
容易发现是
∑ni=1Cin∗(−1)i+1
后面那个就是常见的容斥系数。
这个式子的值是多少呢?
我们可以证明这个式子的值为1。
∑ni=1Cin∗(−1)i+1
=
∑ni=1(Cin−1+Ci−1n−1)∗(−1)i+1
=
∑n−1i=1Cin−1∗(−1)i+1+∑ni=1Ci−1n−1∗(−1)i+1
=
∑n−1i=1Cin−1∗(−1)i+1+∑n−1i=1Cin−1∗(−1)i+2+C0n−1
=
1
也就是说一个人只要拿过金牌,他就只会被计算一次!
这就是这个经典容斥问题为什么是对的证明。
而这个思路也就是容斥的原理:
通过对限制的更改使其变成容易计算的计数问题,再通过配以正确的容斥系数,使得最终得到的结果和原问题一致。
什么是配以正确的容斥系数呢?那么就是在设计容斥时,对每个需要计数的情况应该被计数多少次的考虑!
一般容斥问题都是这样:
要满足n个条件:……
而我们只满足或不满足其中一些条件,其余条件不管,然后进行容斥。
这就是把限制更改来变得更容易计算。
比如经典的错排问题:
有多少排列满足
我们可以看做有n个条件限制:第i个条件是
ai!=i
不等于是个很大的条件,除了i都能取,而等于是个很严格的条件,直接帮助我们确定了一些位置。
因此错排问题是考虑哪些位置不满足条件,其余位置则随便取。
dp[n]=∑ni=0(−1)i∗Cin∗(n−i)!
dp[n]=∑ni=0(−1)i∗n!i!∗(n−i)!∗(n−i)!
dp[n]=∑ni=0(−1)i∗n!i!
dp[n]=(−1)n+∑n−1i=0(−1)i∗n!i!
dp[n]=(−1)n+n∗∑n−1i=0(−1)i∗(n−1)!i!
dp[n]=(−1)n+n∗dp[n−1]
容斥的形式
组合数形式
我们可以给出三种常见的容斥形式,并给出对应例题。
第一种形式是最普遍的,它是
∑ni=1Cin∗f(i)
f表示容斥系数。
在一般的问题中,f都表示-1的多少次方。
现在如果把满足至少1个条件改成至少k个条件,可能会有很多人不知道怎么做了。
但如果你明白容斥的原理,你发现就是
∑ni=1Cin∗f(i)=[n>=k]
知道这个又怎么样呢?
如果允许n^2的复杂度,你根本不需要去想f是多少,你可以让程序帮你算!
你需要构造这个f来满足式子。
我们考虑让n<=m都满足,那么会确定f的前m项。
现在考虑让n<=m+1都满足,就会将f的第m+1项确定。
这个f一定是存在的,而且是可以推的。
写一个段代码预处理这个f当然不困难。
同样我们可以思考更加丧病的容斥问题:
现在对于每个排列,如果有k个位置满足错排,则价值定义为a[k],求所有排列的价值和。
假如你明白了容斥的原理,你仍然可以用容斥的思路去解决这题,你只需构造f来满足:
∑ni=1Cin∗f(i)=a[n]
这样就很方便了!
于是我们现在给一道例题:
这是“玲珑杯”线上赛 Round #17 河南专场某题 。
有n个硬币,初始全部正面朝上。
现在有m次操作,每次把编号是x的倍数的硬币翻面。
最后问多少个硬币正面朝上。
那这个题显然也是容斥题,你先用指数级时间枚举操作,然后求个最小公倍数,接下来问题在于容斥系数怎么定。
有了上面的经验就是构造f满足
∑ni=1Cin∗f(i)=ord(n)
这个f用n^2求没有问题。
斯特林数形式
第一种形式的组合数形式,主要是在枚举约束的子集。
而第二种形式,枚举的是划分的子集。
来看下面这道例题:
bzoj异或图
定义两个结点数相同的图 G1 与图 G2 的异或为一个新的图 G, 其中如果 (u, v) 在 G1 与
G2 中的出现次数之和为 1, 那么边 (u, v) 在 G 中, 否则这条边不在 G 中.
现在给定 s 个结点数相同的图 G1…s, 设 S = {G1, G2, … , Gs}, 请问 S 有多少个子集的异
或为一个连通图?
这个题怎么做呢?
容易想到容斥,容斥方向是用贝尔数的时间来枚举子集划分,也就是不同子集一定没有边,同一个子集可以任意。
这样做就很简单,因为我们可以用异或高斯消元解自由元的个数,来得到方案。
但是容斥系数呢?
容易发现这次不是组合数形式了,它应该是
∑ni=1S(n,i)∗f(i)
S表示第二类斯特林数,S(n,m)的意义是把n个数划分到m个集合。
这个形式下最普通的容斥系数是
(−1)i∗i!
对于这道题来说容斥系数就是
f(i)=(−1)i−1∗(i−1)!
我们可以来证明一下这条式子即
∑nm=1S(n,m)∗(−1)m−1∗(m−1)!=[n=1]
证明与组合数形式类似。
n=1显然成立,我们来说明n>=2时它为0。
∑nm=1S(n,m)∗(−1)m−1∗(m−1)!
=
∑nm=1[S(n−1,m−1)+S(n−1,m)∗m]∗(−1)m−1∗(m−1)!
=
∑nm=1S(n−1,m−1)∗(−1)m−1∗(m−1)!+∑n−1m=1S(n−1,m)∗(−1)m−1∗m!
=
∑n−1m=1S(n−1,m)∗(−1)m∗m!+S(n−1,0)∗(−1)0∗0!+∑n−1m=1S(n−1,m)∗(−1)m−1∗m!
=
0
这就是第二种容斥形式,也可以套容斥系数。
莫比乌斯函数形式
这种容斥其实大家可能见得多了,因为莫比乌斯函数本身就是一种很厉害的容斥系数。
它和组合数意义其实是有点像的。
这种形式一般见于数论问题下,是枚举约数的子集。
我们以phi函数为例。
如何计算n以内和n互质的数的个数呢?
假设
相当于有k个限制,第i个限制要求不是
pi
的倍数。
于是有这么一条式子
∑i|nni∗f(i)
而一般都有
f(i)=μ(i)
我们来考虑一个数会被计算多少次。
显然只有它的因数对它造成影响。
如果这个数是1,因为
μ(1)=1
,所以它被计算了1次,而1和任意正整数互质。
如果这个数有t个质因子n也有,那么会被计算
∑ti=1(−1)i∗Cit
这其实就是组合数形式那个式子嘛!
所以只会被计算1次。
容斥系数寻找的优化
一般配容斥系数都是痛苦的事情(对于非经典的特殊问题)。
而了解容斥原理后进行容斥系数的构造,可以在n^2复杂度内寻找出容斥系数。
这还不够优秀!
打表法
手算或用程序打表,一般容斥系数都有规律,找到规律后在程序中直接套用,不需要花n^2的复杂度了。
例如翻硬币那个问题,我们可以发现它的容斥系数有规律,是
(−1)i∗2i
。
这一般是最有效的方法。
反演法
你可以反演,一般来说去套比较麻烦的话可以多项式求逆(雾。
但是常见的容斥形式都是可以记忆如何反演的。
我们来大致讲讲反演吧。
定义
oi,j=[i=j]
定义
fn=∑ni=1gi∗ai,n
它的反演是
gn=∑ni=1fi∗bi,n
b要满足什么条件?
fn=∑ni=1gi∗ai,n
fn=∑ni=1ai,n∗∑ij=1fj∗bj,i
如果能满足
∑nk=1∑ni=1∑nj=1bj,k∗ak,i=oi,j
上面的式子便可满足。
因此就是这样去构造b。
然后是有二项式反演、斯特林反演这样的东西的。
至于莫比乌斯反演肯定大家都会啊。
这里不进行详细叙述,因为对基本形式反演是有难度的。
组合数形式一般容斥系数会带-1的次幂,其实可以把这个从容斥系数那里拆出来,剩余的可以做二项式反演。
所以就是这样啦!