凸壳操作小记

昨晚下决心学烟火表演,今天终于想明白,昨天还想到一种简单的思考方式。

本文以下凸壳为例,上凸壳的情况容易类比。

Mincowsky Sum

两个点集 \(A,B\) 的闵可夫斯基和定义为 \(\{a+b\mid a\in A,b\in B\}\),即将一个点集中的点看成向量,另一个点集中的点沿着每个向量移动。二维平面上有 \(\{(x_1+x_2,y_1+y_2)\mid (x_1,y_1)\in A,(x_2,y_2)\in B\}\)

对下凸壳的闵可夫斯基和求下凸壳即 \((\min,+)\) 卷积:\((F*G)_i=\min\limits_{j=0}^iF_j+G_{i-j}\)。这显然说明了下凸壳做 \((\min,+)\) 卷积仍下凸,可用于归纳证明凸性。

\((\min,+)\) 卷积可以利用差分+归并快速计算,原理是贪心。具体方法:\((F*G)_0=F_0+G_0\),由于下凸,差分数组(即相邻两点间斜率)单调不降,将两差分数组(从 \(\Delta_1=F_1-F_0\) 开始)归并排序后作为新的差分数组即可。

有时我们可以维护差分数组的连续段(值相同),显然归并时连续段仍连续。为降低时间复杂度,可以用平衡树等可并 DS 维护差分数组(可以最后再排序就只需要合并;平衡树保证随时有序)。

优化 DP 时通常有两种题——自己构造线段树结构来优化 DP(暴力归并)、在给定的树上用 DS 维护。

Slope Trick

Slope Trick 是一种维护凸壳的方法,通过记录初始 \(y\)、斜率和斜率变化的“拐点”来维护图像。对每个拐点仅记录其横坐标 \(x\),表示 \(x\) 右侧的斜率比左侧大 \(1\)(上凸壳则是小 \(1\)),拐点集合是可重集,即同一位置斜率变化量可 \(>1\)。有一个点/一段斜率为 \(0\),它即为 \(\min\) 所在处。

这里给出一些 Slope Trick 对凸壳的操作(忽略对初始 \(y,k\) 的维护;前者通常容易,而后者可从已知斜率处推出,有时甚至不用求出):

  • \(A(x)+B(x)\)\(A(x),B(x)\) 均为下凸函数/一次函数):拐点集合合并。
    • 考虑斜率单调,容易证明下凸函数 \(+\) 下凸函数/一次函数后仍下凸。
  • 前/后缀 \(\operatorname{check min}\):删除 \(k=0\) 点/段的右/左侧所有拐点。
  • 平移/翻转:打标记。

为了维护 \(\min\),可使用对顶堆分别维护 \(k=0\) 点/段左、右侧的拐点集合。合并集合时可使用可并堆。复杂时也许可使用平衡树。

求答案时可以还原图像或记录决策点,后者我还不理解。

这和 Mincowsky Sum 的维护方式本质不同,Slope Trick 维护坐标,而后者维护 \(x\) 坐标上的长度(距离)。这使得它们擅长的操作不同——Slope Trick 擅长 \(+\),而 Mincowsky Sum 擅长 \((\min,+)\) 卷积。Slope Trick 维护的东西似乎更具概括性(考虑坐标相减即得距离,而多段距离累加才得坐标)。

结合

有的题目既要维护 \(+\) 又要维护 \((\min,+)\) 卷积,此时用 Slope Trick 就需要对具体图像细致分讨。

这能忍?(×)
我没学懂。(√)

不妨在拐点集合上做 \((\min,+)\) 卷积,将一个函数看成差分值的若干连续段,考虑它对另一个函数的拐点集合的影响。一段一段插入,对于一段,找到它两端拐点在另一个函数里所对应的拐点,那么只需要将右端拐点及更右统一向右平移段长。对于向右绵延无尽的段,后面就都是它或它完全不出现。

由于 Slope Trick 中的斜率是每次变化 \(1\) 的,故我们有办法让每次都能找到对应拐点。

关于边界:由于 \((\min,+)\) 卷积有左端为 \(0\) 的边界,我们让 Slope Trick 的左边界也是它。右边界不限。

这样就只需要简单的平移了。我觉得它在手推图像上很好用,但用平衡树实现起来或许会很难写(没写过)。

参考

闵可夫斯基和(Mincowsky sum)
【学习笔记】Slope Trick
【APIO2016】烟火表演

2025.7.3

posted @ 2025-12-11 10:21  FirCone  阅读(3)  评论(0)    收藏  举报