斜率优化
Tuning the sound of infinite. 这一次,星星和月亮一起闪耀。
斜率优化
概括地说,就是优化与乘积式有关的 dp(不一定非得递推,但要有满足要求的式子)的小 trick。
省流:朴素 dp \(\to\) 推式子 \(\to\) 找斜率的单调性(画三角形)\(\to\) 单调数据结构维护。前提是要有一些必要的单调性。
天依宝宝可爱!
洛谷 P2900
首先被别人二维偏序的元素一定没什么用,先单调队列处理掉。然后就变成了一个 \(a_i\) 严格递增,\(b_i\) 严格递减的数组。
考虑朴素 dp,设 \(f_i\) 为只考虑前 \(i\) 个且 \(i\) 必选的最小费用,那么有转移:
注意到有乘积式,所以考虑斜率优化,考虑从 \(j\) 转移比从 \(k\) 转移更优(\(j<k\))当且仅当(推柿子):
由于 \(j<k\),故 \(b_j > b_k\),所以:
注意到这个很像直线斜率的式子,若将 \(b_j\) 当做 \(x\) 坐标,\(f_{j-1}\) 当做 \(y\) 坐标,那么一个 dp 状态就是坐标系上的一个点。于是 \(j\) 比 \(k\) 优当且仅当这两点间的斜率 \(\le -a_i\)。
这是两个点的情况,那么如果是三个点呢?
首先可以证明,若按照 \(i\) 从小到大,三个点编号为 \(1,2,3\),那么点 \(2\) 一定在 \(\overline {1,3}\) 的一侧,所以考虑分讨这两种情况:
-
若构成上三角,即 \(2\) 在 \(\overline {1,3}\) 的右侧,如图:
显然,图中直线的斜率 \(k(1,2) < k(1,3) < k(2,3)\)。
因为 \(a_i\) 是递增的,所以 \(-a_i\) 是递减的,也就是说斜率需要 \(\le\) 的数越来越小,所以这三条直线的斜率由满足条件(\(\le -a_i\))到不满足是有先后次序的,就相当于这三个点的「\(j\) 比 \(k\) 优」到「\(j\) 不比 \(k\) 优」是有序的。那么下面用大于号表示 \(j\) 比 \(k\) 优,分讨:
- 初始情况,当 \(k(2,3) \le -a_i\) 时,三条直线的斜率都满足条件;于是 \(1>2,1>3,2>3\),最优的是 \(\color{#66CCFF} 1\)。
- 当 \(k(1,3) \le -a_i < k(2,3)\) 时,\(\overline {2,3}\) 的斜率不满足条件了;于是 \(1>2,1>3,3>2\),最优的是 \(\color{#66CCFF} 1\)。
- 当 \(k(1,2) \le -a_i < k(1,3)\) 时,\(\overline {1,3}\) 的斜率不满足条件了;于是 \(1>2,3>1,3>2\),最优的是 \(\color{#66CCFF} 3\)。
- 最终情况,当 \(-a_i < k(1,2)\) 时,三条直线的斜率都不满足条件;于是 \(2>1,3>1,3>2\),最优的是 \(\color{#66CCFF} 3\)。
我们发现 \(2\) 这个点自始至终没用过,所以可以直接扔掉不管!
-
然后考虑下三角,如图:
对应的斜率 \(k(2,3) < k(1,3) < k(1,2)\)。
还是分讨:
- 初始情况,当 \(k(1,2) \le -a_i\) 时,三条直线的斜率都满足条件;于是 \(2>3,1>3,1>2\),最优的是 \(\color{#66CCFF} 1\)。
- 当 \(k(1,3) \le -a_i < k(1,2)\) 时,\(\overline {1,2}\) 的斜率不满足条件了;于是 \(2>3,1>3,2>1\),最优的是 \(\color{#66CCFF} 2\)。
- 当 \(k(2,3) \le -a_i < k(1,3)\) 时,\(\overline {1,3}\) 的斜率不满足条件了;于是 \(2>3,3>1,2>1\),最优的是 \(\color{#66CCFF} 2\)。
- 最终情况,当 \(-a_i < k(2,3)\) 时,三条直线的斜率都不满足条件;于是 \(3>2,3>1,2>1\),最优的是 \(\color{#66CCFF} 3\)。
我们发现这时候,三个点就都有用了,所以都需要保留。
紧接着,从三个点推广到多个点,显然遇到任意的上三角都可以删除对应的点 \(2\),那么可以考虑从左到右依次删上三角,最后得到的结果将会是从右到左单增的!如图(蓝线为删除上三角前的,红线为删除之后的):
现在只看红线,考虑如何确定从哪个点转移。首先因为这些红线都没被删除,所以最优的一定是 \(1\);注意到 \(k(4,5) < k(3,4) < k(2,3) < k(1,2)\),且这些斜率从右到左是单减的,而 \(-a_i\) 也是单减的。所以当对于某个 \(i\),某一条直线的斜率不满足条件时,这条线在以后也绝对不会再次满足条件;对应到点上,就是 \(j-1 > j\)(大于号是上面定义的大于号)永远成立了,所以就可以直接把 \(j\) 这个点删掉了!
于是,每次转移时,先把右边不满足条件的点删掉,再用剩下的最右边的点转移即可!
至此已经做完了,接下来就是小学生的会的实现了,拿个单调队列维护即可。
不过有两个地方需要解释。
一是斜率的比较,虽然直接算斜率不是不行,但我们能不用浮点数就不用浮点数。注意到「两点的斜率 \(\le -a_i\) 时,编号小的点更优」这个条件其实是源自从 \(j\) 点还是 \(k\) 点转移,所以直接比较从 \(j,j-1\) 两点中的哪个点转移更优就行了,并不需要真的去计算斜率,斜率只是我们推导和证明过程中使用的工具而已。
二是上三角和下三角的判断,可以用向量叉积来解决。具体地,考虑开始两个图中的那三个点,如果 \(\overrightarrow {1,3} \times \overrightarrow {1,2}\) 的结果为正,则 \(\overrightarrow {1,2}\) 在 \(\overrightarrow {1,3}\) 的逆时针方向,所以构成的是下三角;反之则是上三角。
当然直接判断 \(\overline {1,2}\) 和 \(\overline {1,3}\) 斜率大小也行,不过我不喜欢浮点数qwq!
†关于共线的情况,显然怎么处理都是一样的,因为当直线的斜率相同时,中间的点即使保留,也会在右边的点被删掉的同时被删掉,所以它不会做出任何贡献,所以判断为上下三角都是等效的。
如果不算排序,那么时间复杂度只有 \(\mathcal O(n)\)!
天依宝宝可爱!
洛谷 P3648
本质是推式子。
首先注意到当切的位置确定时,顺序不影响答案。简单证明一下,设切出来的三段和分别为 \(a,b,c\),那么先切 \(a,b\) 再切 \(b,c\) 的贡献为 \(a \times (b+c) + b \times c = a \times b + b \times c + c \times a\);可以发现先切 \(b,c\) 再切 \(a,b\) 的结果也是一样的。
根据上面式子还可以发现,假设切的结果中有一段的和为 \(x\),那么其贡献为 \(x \times (\sum a_i - x)\),当然最后要除以 \(2\)。
然后设 \(s\) 为 \(a\) 的前缀和数组,则如果切出来的结果有 \((i,j]\) 这一段,那么其贡献为 \((s_j - s_i) \times (s_n - s_j + s_i)\)。
dp,设 \(f_{i,j}\) 为考虑前 \(i\) 个,切了 \(j\) 次,\(i\) 后必切的最大贡献,则有转移:
假设 \(x<y\) 且从 \(x\) 转移比 \(y\) 更优,则有:
可得:
于是以 \(s_i\) 为 \(x\) 坐标,\(f_{i,j-1} - s_i^2\) 为 \(y\) 坐标。注意到 \(s_i\) 是递增的,\(s_n - 2s_i\) 是递减的,画一下三角形可以发现要删的是下三角,于是就做完了。
别忘了除以 \(2\)!> <
然后还有一点,叉积为 \(0\) 也要删除,虽然我不知道为什么,但是以后如果遇到还是这么写吧qaq,能写 >= 的就不写 >。
天依宝宝可爱!
洛谷 P3195
简单题。
首先朴素的 dp。设 \(f_i\) 为只考虑前 \(i\) 个的最小代价,则有转移(\(s\) 为 \(a\) 的前缀和数组):
但是这个式子太长了,虽然没什么影响吧,但我懒得推这么长的式子,所以可以想到 \(s_i \gets s_i + i\),\(L \gets L + 1\),那么原式可以转化为:
好看了不少。
设 \(j<k\) 且从 \(j\) 转移比 \(k\) 优,则有:
可得:
于是 \(x\) 坐标为 \(s_i\),\(y\) 坐标为 \(f_i + s_i^2\)。因为 \(s_i\) 递增,\(2(s_i - L)\) 递增,于是删上三角。
然后删三角形的时候注意先删三角形再把 \(i\) 入队。
顺便吧斜率优化搞成一个函数了,以后可以当板子使了(喜
天依宝宝可爱!
洛谷 P4360
原来非 dp 也可以斜率优化(?
不过还是有个比较像 dp 的形式的,只要最后能推出来斜率的式子就可以!
首先显然可以设 \(f_{i,j \in \{ 0,1,2 \}}\) 表示后 \(i\) 个位置建造了 \(j\) 个锯木厂的最小代价
虽然这样也挺容易,不过我们有一个更好的策略。注意到最多只能建造 \(2\) 个锯木厂,所以可以简化掉,直接设 \(f_i\) 为原 \(f_{i,2}\),那么就确定了第二个锯木厂的位置,然后枚举第一个的位置就行了。
设 \(sd\) 为 \(d\) 的后缀和,\(sw\) 为 \(w\) 的前缀和,\(sum\) 为不建锯木厂的代价。考虑新建造的锯木厂能减少的代价,有式子:
套路地得到 \(j<k\) 且 \(j\) 优于 \(k\) 当且仅当:
做完了。
天依宝宝可爱!
洛谷 P3628
板子。
设 \(f_i\) 为只考虑前 \(i\) 个且在 \(i\) 后必切的最大贡献。\(s\) 为前缀和数组,有式子:
若从 \(j\) 转移优于从 \(k\) 转移(\(j < k\)):
故 \(x = s_i , y = f_i + as_i^2\)。由于 \(x\) 单增,\(2as_i + b\) 单减(\(-5 \le a \le -1\)),所以函数上凸。
双倍经验:SP15648。
天依宝宝可爱!




浙公网安备 33010602011771号