Slope Trick小记

应该是非常愚蠢的东西,作用是把常数大而且难写的的 \(O(n\log n)\) 优化成常数小而且好写的 \(O(n\log n)\)

例题 CF713C。

先考虑令 \(a_i-i\) 然后转化成不降序列。

很显然最终答案应该是原序列中出现的数,设 \(dp[n][x]\) 表示 \(b_n=x\) 的最小代价就做完了。

转移方程为 \(dp[n][x]=\min_{y=1}^{x}dp[n-1][y]+|x-y|\)

考虑这个过程是在干什么。

好像是在合把两个分段凸函数加起来。

这两个分段函数每一段都是一次函数。

先让我们简化问题,把值域缩小到 \(O(n)\) 看看会发生什么。

根据差分,前缀和以及斜率的定义,我们能发现相当于是把每一段 \([x,x+1]\) 的区间的斜率 \(+1/-1\)

于是我们可以动态维护斜率和这个函数在 \(0\) 处的取值,显然如果斜率大于了 \(1\) 那么这一段将会被置为 \(0\)

因为这是一个凸函数吗,所以斜率一定是单调的。

使用线段树即可做到 \(O(n\log n)\)

容易发现若将一次函数改为 \(k\) 次函数,那么只需要对其微分即可。

而 Slope Trick 是钻了一个空子:斜率最大只有 \(n\)

他维护了每个分割点,表示分割点与分割点直接的斜率差了 \(1\)

于是就可以快速计算某处的值是多少。

不过其实 Slope Trick 维护的是最右边那个分割点上面的值,不影响就是。

以及上述维护方法会受到函数定义域的影响,你需要维护每一处点值,而如果要取任意点值就会寄,或者需要使用平衡树维护。

试试看!CF280E

仍然考虑设 \(dp[n][x]\) 表示 \(y_n=x\)

那么转移应该是:

\[dp[n][x]=\min_{y=x-b}^{x-a}dp[n][y]+(a_i-y)^2 \]

容易发现后面那部分是二次函数,如果仍然使用上述思路的话需要证明 \(dp[n][y]\) 为分段函数。

考虑归纳。对于第一段一定是一次函数。

关于 \([x-b,x-a]\) 可以考虑前缀取一个 \(\min\) 然后再平移。

考虑对于每一段自身平移。能够发现最多只是增加了一段 \(y=x+c\) 的部分,剩下部分仍然是二次函数。

考虑其他段平移到这一段的,能够发现是类似的东西。

所以这个操作结束后一定是分段二次函数。再加上一个二次函数还是分段二次函数。

此时使用上述方法维护每一段的斜率即可。因为仍然是凸函数所以可以使用平衡树维护斜率。(有平移操作)

复杂度 \(O(n\log n)\),但是我觉得即使是 \(O(n^2)\) 的也没什么人愿意去写吧。。。。。。

总结:对于一个凸函数,将其求导后维护其每一段也是可以维护其极值点的,这样会比维护原函数简单一些(?),不失为一种维护方法。

如果要一句话总结那就是 \(f(x)=f(0)+\int_0^x\frac{{\rm d}f(x)}{{\rm d}x}\)

posted @ 2022-09-20 16:32  Prean  阅读(82)  评论(0编辑  收藏  举报
var canShowAdsense=function(){return !!0};