凸壳操作小记
昨晚下决心学烟火表演,今天终于想明白,昨天还想到一种简单的思考方式。
本文以下凸壳为例,上凸壳的情况容易类比。
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
浙公网安备 33010602011771号