斜率优化和决策单调性优化
先推荐几篇比较好的博客吧。
辨析
决策单调性优化适用于满足决策单调性的问题。
斜率优化的适用范围还没学习不清楚。
一般来说斜率优化能做的题目决策单调性优化也能做,但是也有例外,由于过于高深我不知道。除此之外,斜率优化是 \(\mathcal{O}(n)\) 的时间复杂度,而决策单调性优化是 \(\mathcal{O}(n\log n)\) 的。
听说了一种叫 Smawk 的算法,但过于高深所以不学。
斜率优化
简单来说,就是把 DP 的转移式(从 \(j\) 转移到 \(i\))中的变量分离为 常数、只与 \(i\) 有关的项、只与 \(j\) 有关的项、与 \(i\) 有关的式乘 \(j\) 相关的式的项 这四类。随后,通过移项,形成了形如 \(y=kx+b\) 的形式(其中 \(x,y\) 和 \(k,b\) 分别对应着 \(i,j\),或者反过来)。根据处理的方式,可以有两种方式:\(x,y\) 与 \(j\) 有关(\(j\) 形成点)的维护凸壳法,和 \(k,b\) 与 \(j\) 有关(\(j\) 形成直线)的李超线段树法。
辨析4种斜率优化
设转移式为 \(f_x\leftarrow a_xb_y+c_x+d_y\)。其中 \(a_x,b_y,c_x,d_y\) 都代指某个式子(或者说函数)。
- \(a_x\) 单调,\(b_y\) 单调:最经典和简单的一种,只需要使用一个单调队列维护凸壳(也就是说,一边是新的 \((b_y,-d_y)\) 的点的插入,另一边是 \(k=a_x\) 直线在弹出)。(时间复杂度 \(O(n)\))
- \(a_x\) 不单调,\(b_y\) 单调:和上一种差不多。维护一个单调栈,正常插入新的点。但是查找的时候,要改为二分查找。(时间复杂度 \(O(n\log n)\))
- \(a_x\) 单调,\(b_y\) 不单调:使用李超线段树。把 \(x\) 变成查询点,\(y\) 变成修改直线。
- \(a_x\) 不单调,\(b_y\) 不单调:同上。
上述前两种对应着 维护凸壳法,后两种对应着李超线段树法。
维护凸壳法
每个可供转移的 \(j\) 都形成了一个点。枚举到 \(i\) 时,我们的 \(b\) 包含 \(dp_i\),是我们要求的量,而 \(k\) 是已知的量。我们相当于是在求一条斜率固定的直线在与许多点相交时最小/大的截距。而我们注意到(证明待补充)只有在下/上凸壳上的点才有可能被作为转移点,因此我们需要维护凸壳。当我们想要求某个 \(k\) 下的最优的转移点时,注意到(证明待补充)该点两侧的线段斜率分别小于和大于 \(k\)。
一般来说,对于新增的 \(j\),它的 \(x\) 单调递增,所以可以用单调栈来维护凸壳。(就是说,要插入新转移点前,先弹出栈顶被该转移点“囊括”的不再在凸壳上的点)如果查询的 \(k\) 也单调递增的话,就可以用单调队列,每次把已经不可能用的转移点出队,那么队首就是最优的转移点。(\(O(n)\))如果 \(k\) 不递增,就必须用二分在单调栈中找出 \(两侧的线段斜率分别小于和大于 k\) 的点。此时注意单调队列末尾斜率 \(\le/\ge\) 就要 pop,否则若横坐标相等,斜率为 \(\infty/-\infty\) 可能导致二分出错。(\(O(n\log n)\))
倘若 \(x\) 不单调递增怎么办?使用一种在线带修维护凸包的数据结构是可以做的,也或许可以尝试李超线段树的方法。
李超线段树法
每个可供转移的 \(j\) 都形成了一条直线。枚举到 \(i\) 时,我们的 \(y\) 包含 \(dp_i\),是我们要求的量,而 \(x\) 是已知的量。我们相当于是求许多直线在横坐标为 \(x\) 处的最小/大 \(y\)。这可以用李超线段树 \(O(n\log n)\) 实现。
李超线段树的实现方法如下。
线段树只存储 \(mid\) 处是哪些直线(或者是经过了 \(l,r\) 的线段)产生贡献。对于一次新增直线的操作,判断对区间内左边和右边是否有贡献:
- 都有贡献:打上tag,标记永久化(避免太多直线信息下传带来的巨大时空消耗)
- 只有左或右侧有贡献:进入
- 没有贡献:返回
简单吧。
将直线转线段的方法:转化为横坐标在 \([-inf,inf]\) 之间的线段。
决策单调性优化
判定
大名鼎鼎的四边形不等式:
形式化地 \(dp_i=\min\limits_{j=1}^{i-1} w(j,i)\)
如果对于所有 \(a<b<c<d\),有 \(w(a,c)+w(b,d)\le w(a,d)+w(b,c)\) 其中 \(\le\) 表示优于,\(w(x,y)\) 表示 \(x\) 到 \(y\) 的代价(可以不包含下标与 \(x\) 有关数组,因为不等式两边都有这个东西就抵消了)。那么就有决策单调性。
一种等价的变形是:对于任意 \(a<b\),有 \(w(a,b)+w(a+1,b+1)\le w(a,b+1)+w(a+1,b)\)。
实现
有两种实现方式:队列二分 和 分治。
另有一种强大的方式——cdq。
分治
将决策点的值域和决策点数组的位置进行分治。详见 OI-wiki。
好处是,有些代价函数 \(w\) 不是那么好算的,放到分治上会有很好的性质。
最大的缺点是,由于每次先算出 \(mid\) 的决策点,因此前半段的决策点还不知道,如果转移式与 \(dp\) 数组有关就没法做了。此时就要使用队列二分的实现方法。
队列二分
这种实现方式的关键在于 使用三元组 \((l,r,v)\) 来储存决策点数组,从而做到 \(O(1)\) 修改数组的后缀。
详见 OI-wiki。
cdq分治
暂缺。
例题
P4072 [SDOI2016] 征途
推式子,推出一个平方和。然后需要跑 \(m\) 遍 dp,每次基于上一次的数组,所以可以用分治法(其实这题正解是斜率优化)
P3628 [APIO2010] 特别行动队
典题。难点在于推四边形不等式,以及注意队列写法的决策单调性优化不要写错(我没有特判“本次没有修改转移点数组”的情况)
P3515 [POI 2011] Lightning Conductor
首先式子可以写成 \(p\ge a_j+\sqrt{|i-j|}-a_i\),那么我们求出右侧式子的最大值再向上取整就可以了。
我们从左往右、从右往左做一遍,这样每次就只需要考虑 \(i\) 一侧的 \(j\),式子也变成了(以从左往右为例)\(w(j,i)=a_j+\sqrt{i-j}-a_i\)。\(w(j,i)\) 是满足决策单调性的,因为 \(\sqrt{i-j+1}+\sqrt(i-j-1)\le 2\sqrt{i-j}\)(\(\sqrt{x}\) 函数的上凸性)。那么我们就可以直接做了。
请注意,不能把 \(\lceil\rceil\) 套在 \(\sqrt{i-j}\) 的外面,因为刚才的证明中依赖了 \(\sqrt{x}\) 函数的上凸性,如果是 \(\lceil\sqrt{x}\rceil\) 就不能保证这一性质了。所以只能把 \(\lceil\rceil\) 放在最外层。

浙公网安备 33010602011771号