Min-Max 容斥

1 引入

Min-Max 容斥,又称最值反演,是一种用于特定集合在已知最大值或最小值的情况下求另一者的算法。

举个例子来讲,我们不难注意到以下式子:

max(a,b)=a+bmin(a,b)

这就是 Min-Max 容斥的二元基本形式。容易发现,只要我们对所有数取相反数,那么用最小值求最大值和用最大值求最小值的算法本质上相同。下文只讨论用最小值求最大值的方法。

2 基本形式

max(S) 表示集合 S 中的最大值,min(S) 表示集合 S 中的最小值,则有:

max(S)=TS,T(1)|T|1min(T)

证明:与二项式反演类似的,我们定义一个函数 f(i) 表示一个关于集合大小 i 的函数,且满足 max(S)=TS,Tf(|T|)min(T)。接下来我们对 S 中的元素从大到小排序为 x1,x2,,xn,则对于 xi, 其在等式左边的贡献为 [i=1],在右边的贡献为 j=0i1(i1j)f(j+1)。若等式成立,则有:

[i=1]=j=0i1(i1j)f(j+1)

即:

[i+1=1]=j=0i(ij)f(j+1)

那么设 F(i)=[i+1=1],G(i)=f(i+1),则会有:

F(i)=j=0i(ij)G(j)

对这个式子进行二项式反演可得:

G(i)=j=0i(1)ij(ij)F(j)=(1)i

所以 f(i)=G(i1)=(1)i1,所以原式成立。

3 推广形式

kmax(S) 表示集合 S 的第 k 大值,则有:

kmax(S)=TS,|T|k(1)|T|k(|T|1k1)min(T)

证明:依然设一个函数 g(i) 表示一个关于集合大小 i 的函数,且满足 kmax(S)=TS,Tg(|T|)min(T),并且对 S 中元素从大到小排序。那么对于 xi,其对等式左边的贡献为 [i=k],对右边的贡献为 j=0i1(i1j)g(j+1)。若等式成立,则有:

[i=k]=j=0i1(i1j)g(j+1)

同样的,设 F(i)=[i+1=k],G(i)=g(i+1),那么有:

F(i)=j=0i(ij)G(j)

根据二项式反演可得:

G(i)=j=0i(1)ij(ij)F(j)=(1)ik+1(ik1)

g(i)=G(i1)=(1)ik(i1k1),所以原式成立。

4 应用

4.1 应用场景

你可能会觉得这东西很弱智,毕竟最大最小值都可以直接求,不必这么麻烦。事实上,它真正有用的场景是在期望中,因为 Min-Max 容斥在期望中也是成立的:

E(max(S))=TS,T(1)|T|1E(min(T))E(kmax(S))=TS,|T|k(1)|T|k(|T|1k1)E(min(T))

这就给了我们非常大的操作空间去求出最大值甚至 k 大值的期望。

另一方面来讲,我们知道,lcm 是对所有数字的质因子指数取 max,而 gcd 是对所有数字的质因子指数取 min,于是我们对指数做一个 Min-Max 容斥即可得到:

lcm(S)=TS,Tgcd(T)(1)|T|1

这也是 Min-Max 容斥的应用之一。

4.2 例题

下面的两道例题正好运用了上面所说的两个应用。

例 1 [HAOI2015] 按位或

Link

容易发现,假如我们令 ti 表示第 i 位变为 1 的时间,那么答案就是 E(maxti)。根据 Min-Max 容斥有:

E(maxti)=(1)|T|1E(minti)

所以我们枚举出位的集合 T,问题就在于求出这些位当中第一次出现 1 的期望时间。考虑去求出这些位中出现 1 的概率 p,实际上就是 1 减去这些位上不出现 1 的概率。要让这些位上不出现 1,只能说明我们选的是 T 的补集的子集,我们只需要求出选出这些子集的概率之和即可。

求子集的概率之和就很简单了,直接做一遍高维前缀和即可得出答案,最后算出对应的 p,那么 E(minti) 就等于 1p。最后根据这个算出 E(maxti) 即可。

例 2 [BZOJ4833] 最小公倍佩尔数

Link

下文中为了方便,记原来的 f(n)Fne(n)En

首先 Fn 的通项公式其实是可以求出来的,不过没有什么用,对做出此题没有帮助。不妨换一种思路,根据题意推一下 Fn 的递推式。首先显然有如下式子:

(1+2)n=(1+2)(1+2)n1=(1+2)(En1+2Fn1)=En1+2Fn1+2En1+2Fn1

于是可以得到:

En=En1+2Fn1Fn=Fn1+En1

由下面的式子可以得到 En1=FnFn1,带入上面的式子可以得到 En=Fn+Fn1,再带回下面的式子可得:

Fn=2Fn1+Fn2

这就是 Fn 的递推公式。接下来我们有一个性质,如果一个数列的递推公式为 Fn=aFn1+bFn2gcd(a,b)=1,那么 gcd(Fn,Fm)=Fgcd(n,m)。于是我们就可以将 gcd 转到下标上,方便操作。

不过题目中给出的要求是求 lcm,而我们现在求出的是 gcd,于是不难想到 Min-Max 容斥的操作:

lcm(S)=TS,Tgcd(T)(1)|T|1

在此题中它表示为:

g(n)=TS,Tfgcd(T)(1)|T|1

其中 S 表示 1n 的所有整数构成的集合。现在下标上有 gcd,非常不好看,我们考虑直接枚举 gcd(T)

g(n)=TS,Ti=1nfi(1)|T|1[gcd(T)=i]

接下来将所有东西挪到 fi 的指数上:

g(n)=i=1nfiTS,T[gcd(T)=i](1)|T|1

现在的问题就是求 TS,T[gcd(T)=i](1)|T|1,这是一个经典的 gcd 容斥,令 f(i)=TS,T[gcd(T)=i](1)|T|1h(i)=TS,T[igcd(T)](1)|T|1,则根据莫比乌斯反演可得:

h(i)=idf(d)f(i)=idh(d)μ(di)

然后回来看 h(i) 的取值,我们求出 n 范围内 i 的所有倍数,那么我们可以在这里面任意选择出集合 T。理论上讲由于 T 的大小是奇数还是偶数的情况数是一致的,所以所有的 (1)|T|1 会抵消掉。但是由于 T,所以偶数的情况会少一种,所以会剩下一个奇数情况没有消掉,于是 h(i)=[in]

所以可以得到 f(i)=idμ(di),改枚举 ddi 可得:

f(i)=j=1niμ(j)

带回原式可得:

g(n)=i=1nfij=1niμ(j)

暴力求的话肯定不行。考虑到对于一个 fi,它给 g(i)g(n) 做贡献时,上面的指数 j=1niμ(j) 最多只会改变 ni 次,所以我们只需要在它做出的贡献改变的时候乘上新的贡献,最后再做一次前缀积即可得出正确的 g(n)。总复杂度 O(nlnn),可以通过。

posted @   UKE_Automation  阅读(25)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
点击右上角即可分享
微信分享提示