专栏文章

关于凸性的 things(wqs + slope trick + 闵可夫斯基和)

算法·理论参与者 2已保存评论 1

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
1 条
当前快照
1 份
快照标识符
@mlquvngu
此快照首次捕获于
2026/02/18 01:07
3 周前
此快照最后确认于
2026/03/10 01:25
23 小时前
查看原文

能够解决的问题

一些满足凸性的东西。

算法

wqs 二分

能解决的问题

让你求恰好用 kk 步解决问题的最小代价,而且最小代价关于步数的函数有凸性。
如:求恰有 kk 条白边的最小生成树,求把序列恰好分成 kk 段的最小权值。
其实就是求一个凸函数 f(x)f(x) 的某一项。

思路

注:如有看不懂可以跟下文算法步骤一起食用。
下面假设上凸,如下图:
假如我们要求红点处的值。我们可以考虑:假设 f(x)f(x) 与一条直线相切,且我们知道那条直线的方程和切点,那是不是就能求了?(这里的条件看丝柯克似苛刻,其实不然,我们往下看)
那么我们需要:
  • 直线斜率 kk
  • 直线截距 bb
  • 切点横坐标 xx
我们发现,由于 f(x)f(x) 凸,所以随着 kk 减小,xx 增大(类比上图中 k1>k2k1 > k2),所以我们每次枚举,然后求切点横坐标,通过二分找到要求的点的切线对应的斜率 kk(上图中就是找到一个 k1>k>k2k1 > k > k2kk)。
然后我们有了 kk,如果刚好顺便求出了 bb 那么不就求出了切点了吗?

算法过程

上述过程看似每一步都像拼凑的,可是我们写出算法过程:
  1. 二分出一个斜率 kk
  2. 根据我们的思路,f(x)=kx+bf(x) = kx + b,那么 b=f(x)kxb = f(x) - kx
  3. 由于直线是切线,则 bb 应最大(可以看图理解),所以我们求出最大的 bb 与其对应的 xx 即可。
  4. 于是求 f(x)f(x) 的某处的值变成了求 y=f(x)kxy = f(x) - kx 的最大值点。
与问题结合:f(x)f(x) 是用 xx 步解决问题的最下代价,那么 y=f(x)kxy = f(x) - kx 即为每用一步就付出 kk 的代价情况下的最小代价(不限制步数)。这个东西一般都能非常快的求出来。

不可忽视的细节

考虑下图:
此时红点与相邻两点贡共线。我们发现此时你如果二分到斜率 kk,则你有可能求出的切点横坐标偏小,你就找不到切点横坐标为指定值对应的斜率
我们的解决办法是:每次保证找到的切点永远最靠左,那么我们就可以二分出最大的切点横坐标不大于要求的点的横坐标的斜率(有点绕,建议看看图),那么问题迎刃而解。

例题

结合算法过程,那么我们只要二分 xx,然后求每选一条白边就给 MST 加 xx 的权值的情况下的最小生成树(也就是白边权值 vv+xv \gets v + x),直到此时恰好选了 kk 条边,则此时就是答案。
注意细节,Kruskal 时同样边权的边黑边在先,这样就可以尽可能让白边数量少(切点横坐标小)。

slope trick

下面的讲解以下凸为例。

能够解决的问题

dp 的某一维满足凸性。

前置知识:凸函数的闵可夫斯基和

点集 AABB 的闵可夫斯基和为 {(x,y)x=x1+x2,y=y1+y2,(x1,y1)A,(x2,y2)B}\{(x, y) | x = x_1 + x_2, y = y_1 + y_2, (x_1, y_1) \in A, (x_2, y_2) \in B\}
考虑下图(红、绿色凸函数的拐点集的闵可夫斯基和是黄色凸函数的拐点集):
观察被标成相同序号的边:由于两个涂蓝的部分都是平行四边形,所以黄色凸函数的各部分斜率就是红、绿两色各部分斜率的归并(可以自己也多花几个图辅助理解)。
于是我们得到结论:两个凸函数的闵可夫斯基和的斜率是各自斜率的归并

维护拐点的情况

思路

顾名思义,把 dp 有凸性的那一维通过“拐点”与正无穷大处的斜率维护。
拐点:右侧斜率比左侧大一(同一个地方可以有多个拐点),如下图(红点为拐点):

例题

dpi,jdp_{i, j} 为考虑 1i1 \sim i,使 aia_i 变为 jj 的最小代价。
通过天蒙与打表:jj 那一维有凸性。
考虑从 dpi1dp_{i - 1}dpidp_i
  • 转移式:dpi,j=minkjdpi1,k+aijdp_{i, j} = \min_{k \leqslant j} dp_{i - 1, k} + |a_i - j|
  • 后面的部分是定值,而前面的部分:
蓝色的部分的 jj 对应的最优的 kk 就是 jj 本身;绿色的 jj 对应的最优的 kk 是函数的最小值。所以转移等价于:
  1. 绿色的部分直接变成黄色的部分:纵坐标是函数最小值的常函数。
  2. 整个函数加上 y2y_2
于是最终变成灰色图像。
观察两步转移,我们发现函数最右侧的斜率永远为 11(常函数+绝对值函数)。所以转移等价于:
  1. 删掉最大的拐点。
  2. aia_i 处加两个拐点。
  3. 正无穷大处的斜率保持不变。
于是用一个堆维护拐点,就完了。

维护差分数组(斜率)的情况

思路

顾名思义,把 dp 有凸性的那一维通过差分数组与 x=0x = 0 处的值维护。

例题

显然的 dp 是树形背包。
又一次通过天蒙与打表:jj 那一维有凸性。
dpu,jdp_{u, j}uu 的子树内染了 kk 个点的最小代价,观察转移:minson(dpu,j+dpson,k)dpu,j+k\min_{son} (dp_{u, j} + dp_{son, k}) \to dp_{u, j + k},不就是 dpudp_udpsondp_{son} 的闵可夫斯基和吗?
于是每次归并差分数组(用一个可并堆),做完了。

制作不易,点个赞呗~

评论

1 条评论,欢迎与作者交流。

正在加载评论...