容斥原理

概述

  • 容斥原理是正难则反思想的实践产物。(23.1.15 upd:存疑)

  • 即,在正向求解问题过于困难时,考虑逆向求出不合法方案数,然后用总方案数减去以得到合法方案数。

  • 大体上,可以分为以下两类:

子集容斥

  • 一般的容斥原理指的就是子集容斥。其是如下的一种容斥:

  • 不妨称“合法”为满足 k 个条件,并抽象为条件的集合,则有如下的一般形式:

ans=S=full(1)|S|×resS

  • 这里 resS 为满足的条件包含 S 的方案数。

  • 用自然语言描述,就是所有情况(所有条件都爱满足不满足的情况)减去至少有一种条件不满足的加上有两种的减去有三种的...

  • 证明基于二项式定理,等等吧...

P1450 [HAOI2008] 硬币购物

  • 题意:

    • 给出 c14

    • Q 组询问,每组询问给出 num14C,求关于 a14 的方程组 i=14ai×ci=C 的合法解集数。

    • 所谓合法解集,即满足 i[1,4],ai[0,numi] 的解集。

  • 数据范围:Q103,C105

  • 首先显然可以背包,但也同样的,背包显然会 T。

  • 本题有 exgcd 做法,大概思路是预处理 c1,c2c3,c4 两组的 aici+ai+1ci+1=gcd(ci,ci+1) 的不定方程,然后对每个询问暴力枚举两组分别付了多少钱,设法 O(1) 计算方案数。

  • 考虑建立容斥模型:所有方案-有一个超限+有两个超限-有三个超限+有四个超限的方案数。

  • 则问题相当于怎么求有 k 个超限的方案数。发现此时的选取个数上界变成了选取个数下界;考虑到这个下界肯定 0,我们用这么一个办法:把它减去!

  • 即,将 C 减去 i=14reqi×ci,于是问题变成完全背包求方案数。

  • 发现这个完全背包没有必要每次都做,可以预处理。于是得解,复杂度 O(4C+24Q)

  • 事实上,这一做法可以推广到一般的不定方程非负整数解集计数。

P5664 [CSP-S2019] Emiya 家今天的饭

  • 题意略。

  • 发现这个主要食材的问题看起来就很状压,但 m 太大了,全无机会。考虑容斥,即爱超不超-至少一种食材超了+...。

  • 发现一种食材如果不合法,其一定占据了至少 n2+1>n2 道菜,于是同时至多只有一种食材不合法。

  • 哦吼!那么容易做一个简单 DP dpi,0/1 表示考虑了前 i 种烹饪方法,是否已经做过菜的总方案数,从而得出满足前两个条件的总方案数。

  • 然后再暴力枚举超限的食材,做一个 dp 如下:

    • 状态设计:tpi,j,k 表示考虑完前 i 种烹饪方法,做了 j 道用这个食材的菜,共做了 k 道菜的方案数。

    • 初始化:dp0,0,0=1

    • 状态转移方程:dpi,j,k=dpi1,j,k+dpi1,j1,k1×ai,material+dpi1,j,k1×ai,else。滚维即可。

  • 总复杂度 O(n3m),还是超了一些。注意到我们本质上不关心 j,k,只关心 [2j>k],换言之关心的是 [2jk>0]

  • 起一个偏移,然后把这两维变成一维,总复杂度 O(2n2m),足够通过本题。

P2158 [SDOI2008] 仪仗队

  • 题意:求 i=1nj=1n[gcd(i,j)=1]。这是转化并舍弃了一点细节后的题意。

  • 数据范围:n4×104

  • 式子不是很美妙,考虑拆一拆:

i=1nj=1n[gcd(i,j)=1]=1+i=1nj=1i1[gcd(i,j)=1]+i=1nj=i+1n[gcd(i,j)=1]=1+2×i=1nj=1i1[gcd(i,j)=1]=1+2×i=1nφ(i)

  • 首先利用对称性把它化成可欧拉函数化的式子,然后再代入欧拉函数做变换。一开始 +1 是因为漏算 gcd(1,1)(其他 i=j 的在变换中直接被舍弃了),最后 1 是因为 φ(1) 也计入了并且 ×2 了。

  • 众所周知,φ(i) 是可以线性筛的,于是我们有线性复杂度。

  • 什么?没容斥?你别急。

P2398 GCD SUM

  • 题意:求 i=1nj=1ngcd(i,j)

  • 这大概算是一个定式。有两种思路:

  • 转化成上面的问题。即:

i=1nj=1ngcd(i,j)=d=1ndi=1ndj=1nd[gcd(i,j)=1]=d=1nd(1+2×i=1ndφ(i))

  • 预处理 φ 的前缀和,仍然是线性的。

  • 基于调和级数的暴力:

  • 考虑求 div(x) 表示 i=1n[xi]。此部分的复杂度是 O(n)

  • 显然,div(x)2 就是所有 gcdx 的倍数的数对的数量。考虑容斥之,可以倒序计算,O(nlnn)

  • 但如果我们用一般的容斥原理,那么实质上是对...总之似乎可以用莫反+狄利克雷前缀和优化到 O(nlnlnn)

P1447 [NOI2010] 能量采集

  • 题意略。我们约定 nm,若不然,交换之。

  • 首先从 P2158 [SDOI2008] 仪仗队 我们知道这种题目显然可以只做对称的两部分中的一部分。

  • 考虑枚举 d,不妨记 k(d)gcdd 的数对数量,稍微化下式子有 ans=d=1n(k+1)2

  • 于是问题变成求 d=1n(i=1nj=1m[gcd(i,j)=d]+1)2

  • 首先我们还是可以进行一个 /d 将其转化到 φ 上,但 n,m 不等使得我们的旧有手段失效了,毕竟对称性完全不存在。

  • 考虑转而多支付一个 log 或者之类的。哦,可以容斥!

  • 套用上面的调和级数方法,得解。

P2567 [SCOI2010] 幸运数字

  • 题意略。

  • 暴力求出所有幸运号码,然后开始暴搜容斥。

  • 几个剪枝技巧:

    • 若幸运数字中 ab,删去 b

    • 由此,我们暴搜中的 v 应当互相不为倍数;从而对于 v×3>R 的可以直接退出,因为任意 lcm 至少是 v 的三倍。

P3160 [CQOI2012] 局部极小值

  • 参看状压 DP。

代表元容斥

  • 子集容斥虽好,但显然有一个致命缺陷:子集总数是指数级的。

  • 当条件很多的时候,其根本没有表现的机会。

  • 代表元容斥给出了一种解决方法:给条件规定一个“顺序”(这一顺序不能违反条件本身的拓扑关系),按顺序考虑这些条件,从而将所有方案分为“违反了第 k 个条件的”。

  • 换言之,它只关心方案“最早违反的条件”。可以写出如下的一般形式(fsta 为从初始状态到 sta 的合法方案数,gsta,sta 为从 stasta 的任意方案数):

fsta=gstart,stasubstafsub×gsub,sta

AT_dp_y Grid 2

  • 题意:给出一张 n×m 的网格图,有 K 个点不能经过,求只向下/向右走的条件下(OI 坐标系),从 (1,1) 走到 (n,m) 的方案数。

  • 数据范围:n,m105,K3×103

  • 没有不可经过点的话,这是一个经典的组合数学问题。考虑处理这些点,显然子集容斥根本不可做。

  • 注意到对于非法点的访问肯定是有顺序的:(x,y) 成为代表元,即首个访问的非法点,当且仅当所有 (x,y)(不能同时取等)的非法点都没有访问时,才有可能。

  • 这是一个很松的偏序。随便车一个拓扑序,定义 fx,y,x,y 为从 (x,y) 走到 (x,y) 的合法(这里指路上不经过非法点,起止点不算)方案数,gx,y,x,y 为对应的任意方案数,于是有:

fx,y,x,y=gx,y,x,y(x,y)<(x,y)<(x,y)fx,y,x,y×gx,y,x,y

  • 这里所有 (x,y) 都是关键点,即 (1,1),(n,m) 或非法点。坐标之间的 < 是以上面的方式定义的,即 xx,yy(x,y)<(x,y),两个不等号不能同时取等。

  • 于是 f1,1,n,m 即为所求,复杂度 O(K2)

P2595 [ZJOI2009] 多米诺骨牌

  • 题意略。说实话,其他状压和容斥在它面前都黯然失色。

  • 首先考虑无限制的情况,即,不考虑跨线只考虑障碍。那么这是一个非常板的轮廓线 dp,我们容易做到 O(nm2m)

  • 容易想到把一维用 dp 解决,另一维用容斥。乍一看很对,上面的 dp 也可以加一个 0/1 辅助维以确保相邻两行能够横跨,然后呢?

  • 不妨记 validl,rlr 列填完,保证合法即每两行之间都有骨牌横跨的方案数。好了,不用说了。

  • 我们看到,这里对列之间的横跨容斥的时候,不管是用子集还是用代表元,都有一个问题:左边的方案自己在行间是合法的,右边也一样,但是考虑不到两者本身不合法拼起来合法的方案。

  • 考虑进一步以退为进,对两维都容斥!

    • 这里我们有所有 dp 值,换言之,我们有 allu,l,d,r 表示子网格 (ud,lr) 的只考虑障碍不考虑横跨的总方案数。

    • 时间复杂度?暴力枚举左上角和右边界,可以对所有下界一口气求出,总复杂度为 u=1nl=1nr=ln(nu+1)(rl+1)2rl+1,直接跑程序算出复杂度为 2×108,完全能接受。

  • 首先肯定不可能都子集容斥,因为时间炸了。考虑使用代表元容斥,我们先给出一个子集容斥套代表元容斥的正解,再谈论其他嵌套方式:

    • 暴力枚举哪些列间不合法。这是子集容斥。

    • 然后开始代表元容斥,这里我们需要把被分割的多个块合起来考虑行的合法性。

    • 定义 fi 表示前 i 行填完,内部的所有行间都合法的方案数。则有式子为 fi=g1ij=1i1fj×gj+1i,这里 gl,r 表示第 lr 行随便填(内部行间合法与否无所谓)的方案数,显然其等于各块的 all 的连乘。

    • 我们可以在用到它的时候临时将之算出,带一个 ppc 的常数,算 f 的过程本身显然是 O(n2),故总复杂度为 O(2nn2),可以接受。

  • 考虑类似的手法,同时进行两个代表元容斥,外列内行,此时左上两个都满足,右上满足行,左下满足列,右下无限制。注意到主要问题在于此时内层的 g(内层小写,外层大写)不再是 all 的连乘,而是左侧的受列限制的 all 和右侧的正常 all 的连乘,先不管这个算一下复杂度,发现是 O(n4),因为外层每枚举一个列间,就需要 O(n) 种关于行完全合法但关于列随意的 G,而每个 G 又需要做单独的对行代表元容斥 O(n2)

  • 回过头来考察内层的 g 所需的左侧受列限制的 all 怎么来,事实上只要单独对列容斥就好啦。需要的受列限制的 alll=1,其他三维不可知,暴力枚举上下界然后做代表元容斥,显然可以一次把所有右界对应的都求出来,O(n4)。乍一看复杂度瓶颈在 dp,所以还是没变,但实际上注意到真的用到无限制 all 的地方只有内层的“右侧的正常 all”和这里单独对列容斥用到的 all...啊前者确实是顶着右界的,可以只求顶着右界的 all,可惜后者不行。后者还是任意位置的,故复杂度瓶颈确实没变。

  • 如果外代表元内子集呢?更劣的一种实现,因为其内部还是需要“左侧的受列限制的 all”,某个切割出的块的总方案求法也是左受限 all 乘右任意 all,其余流程都一样。

posted @   未欣  阅读(322)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示