slope trick

slope trick 用于优化一类二维 dp 问题。对于 \(f_{i,j}\),我们希望对于所有 \(i\)\(f_i\) 是连续的分段函数,并且具有凸性。特别地,我们希望每一段的斜率(即差分)都是整数,当然对于贡献是整数的题目来说这通常还是比较显然的。

显然有一些固有的函数满足上述性质,例如绝对值函数、一次函数等。


思想上,我们尝试把 \(f_i\) 写成凸包的形式。我们考虑维护一个可重集表示所有斜率发生变化的位置,其中为了表示变化量,我们将变化的位置写 \(\Delta k\) 次就可以意味着斜率的变化量为 \(\Delta k\)

当然这并不能唯一确定凸包。所以我们还需要维护一根线的表达式,不妨取最左侧那一根,维护它的斜率与截距 \(k_0,b_0\),我们可以认为将可重集中的所有位置从小到大写出来之后,\(i\) 上的斜率是 \(k_0+i\)(对于下凸)。

考虑我们得到了这个可重集之后可以简单地完成很多麻烦的转移操作:

  • 合并两个满足性质的函数:直接合并可重集,然后 \(k_0,b_0\) 也对应相加。
  • 取前后缀 \(\min\):去掉 \(k<0\) 或者 \(k>0\) 的部分,维护 \(k_0,b_0\)
  • \(\min\):提取 \(k=0\) 的部分。
  • 平移/翻转:修改可重集打全局标记,维护 \(k_0,b_0\)

具体还需要根据实际情况选择数据结构和数据结构意义刻画。通常我们使用堆和平衡树等能够维护大小关系的数据结构来维护这个可重集。

P4597 序列 sequence

考虑显然的 dp,有:

\[f_{i,j}\gets \min\limits_{k\le j} f_{i-1,k}+|a_i-j| \]

归纳容易证明 \(f_i\) 是下凸的。

考虑转移是我们要塞一个绝对值函数进去,然后删掉所有 \(k<0\) 的部分。

注意到我们可以暴力维护可重集,原因是每次插入只会导致断点 \(a_i\) 处一个点上的斜率增加 \(2\)。所以我们直接维护一个大根堆表示可重集中从左到右的情况,同时维护 \(k_0,b_0\) 即可。

注意到每次操作都必然会导致 \(k_0\) 减小 \(1\)。但由于前缀 \(\min\) 的存在,我们恰好会弹掉一个断点来使得 \(k_0\) 变回 \(0\)。这些优美的性质使得我们每次操作时只需弹出一次最左侧的斜率恰为 \(-1\) 的线段,并且答案就是 \(b_0\)

posted @ 2025-07-18 09:46  Shunpower  阅读(8)  评论(0)    收藏  举报