Slope Trick 是二维 dp 的优化方法,用于优化一个凹/凸函数。
我们利用分段点集合来表示这个函数。比如一个凹函数,−2,2,2 表示在 −2 的时候函数斜率 −1,在 2 的时候 −2,然后还知道最后 k=2,b=−6。
P4597 序列 sequence
P4597 序列 sequence。
2024 年做这道题的时候,并不知道什么 Slope Trick,只是觉得这是一个技巧,可以水过好几倍经验。
fi,j 表示处理到 i,把它改成 j 的最小操作次数。
fi,j=k≤jminfi−1,k+∣ai−j∣
fi(x) 是一个凹函数。因为 i=1 显然,i>1时,两个函数相加仍然是。
我们每一次输入 ai 都会让 ai 的斜率增加 2。然后每一次都需要 pop 来保证维护的是斜率最小的点,每一次的贡献就是最小值的点和当前的差的绝对值。
代码
C++
for (int i = 1; i <= n; i++)
{
int x; cin >> x;
q.push(x); q.push(x);
ans += q.top() - x;
q.pop();
}
P3642 [APIO2016] 烟花表演
P3642 [APIO2016] 烟花表演。
设 gi,j 表示在 i 这个点,所有儿子节点需要 j 秒引爆的代价。这是一个凹函数。
然后我们还需要一个 fi,j 表示 i 这个子树所有节点在 j 秒引爆的代价。
设 gx 的最优区间为 [L,R]。
fi(x)=⎩⎨⎧gx+wgL+l−(x−L)gLgL+x−R−lx≤LL<x≤L+lL+l<x≤R+lR+l<x需要把这条边距离缩短为 0L 时间引爆需要修改的路径长度存在刚好能走完后恰到 x 的出发时间需要延长的路径长度
其实后面的我也不知道怎么去想到,或许我们只是想优化一下转移。
- 第一种是直接将 gx 的图像向上平移 l。
- 第二种是插入 fx=−x+gL+L+l 这条斜率为 −1 的线。
- 第三种是把原来的 [l,r] 向右平移了 l。
- 第四种是插入 fx=x+gL+x−l−R 这条斜率为 1 的直线。
接下来就用 Slope Trick 来维护了,用一个大根堆来维护。
再加上这个函数是连续的这个性质...
- 第一种是直接将 f0 加上 w。
- 第二种和第三种直接弹出 L,R 然后再塞入 L+l,R+l,此时斜率为 −1 的线段被延长,然后 [L+l,R+l] 的斜率为 0。
- 第四种是合并完所有子节点的堆之后弹出 子节点个数−1 个最大的点。
最后的答案就直接在根节点的堆里面,用 f0 减去所有拐点坐标就行了。