这一步的精髓就是分离 i 相关与 j 相关的部分。之所以我可以这样概括是因为斜率优化 DP 通常都是一层循环枚举下标,内层循环得到一个最值去更新外层的 DP 值,那么我们的重点关注对象就是这个内层取最值的部分(实际上,这部分也是我们方程的主体)。我们把最值柿子的 i 和 j 分离开,原因是之和 i 相关的部分我们根本就不用带到最值循环里去算,这是为了斜率优化的处理。
首先有一个这样的性质,我们把这个问题考虑成一条斜率 k_i 的直线,从右向左移动,然后在这个平面里面碰点。第一个碰到的点就是答案点。感性理解,剩下的点肯定需要我继续往左移才能碰到,我这个直线越向左,它与 y 轴交点就越大,也就是截距越大。再进一步,一条直线向左移其实等于一条直线向上移,所以你这个向左其实等于沿着 y 轴逐渐升高。
那么我们怎么找到这个 g 呢?我们发现如果 k_i 飘忽不定,那么我们就要用平衡树之类的睿智玩意儿去找这个 g 了,然而本题中 k_i 绑定了 s_i,s_i 存在单调递增,那么 -s_i 单调递减,前面又有一个 -2,那么负负得正,k_i 存在单调递增。
那也就是说我们前面用不到的 g 后面都不可能再用了了。具体来说,对于斜率小于 k_i 的在队首的需要弹掉,然后考虑将 i 放进队列尾,为了维护下凸壳性质,如果凸壳中最后一条线(队列中最后两点连线)斜率大于队尾与 i 连线的斜率,就需要弹掉队尾。画一个下凸壳并考虑斜率为 k_i 的线在斜率增大或减小时碰到凸壳的情况,以及新加入的 i 点如何使凸壳不合法,可以很轻松的得知弹队首或队尾的条件。
我们的具体操作如下:
把第一个点加入双端队列。
从队首拿出两个点,如果这两个点 (x_a,y_a) 和 (x_b,y_b) 之间的线原线的斜率小于等于现在这个斜率 k_i,那我就弹掉队首的那个点直到只有一个点了或者可以满足了。这里为了避免除法,我们可以把 \frac{y_b-y_a}{x_b-x_a}\leqslant k_i 这个式子中下面的 x 移到右边。这里不需要给减法绝对值的原因是我的队列自动维护了后面的那个点 y 值更大(见第三个操作),斜率优化的单调性告诉我 x 单调。
在这里进行转移,这个地方队首的点一定是我们需要的那个好东西。
我们来维护凸壳。我现在往里面加点 (x_i,y_i),我们就必须保证之前定义的 K(r-1,r)<K(r,i),这里的 r 是队尾。如果不等式不成立我就一直弹队尾的点,直到可以成立或者弹到只有一个点了。那么这里由于 x 是单调的,如果 y 出现了不单调,那么这个破坏我们单调性的烦人的点绝对不在凸壳上(否则就会出现凹进去的地方,那就出大问题了),这也就解决了操作二里面的绝对值问题。