Slope trick优化dp

适用于一类dp值关于下标的函数是连续函数,分段函数,凸函数,每一段需要是一次函数,需要是整数斜率。常见于一些最小调整代价题,因为经常会有|xy|这种典型符合上述要求的函数出现,而且这类dp通常会有对应下标相加的形式出现。
我们考虑通过最右一段的一次函数y=kx+b,和前面的分界点来表示这个函数。要求相邻段之间函数的斜率差为1,也就是说如果真正的函数里有相邻两段之间斜率差不为1的话,这个分界点要在数据结构里出现多次。比如下面这个函数:

f(x)={xx<111x<12x1x1

那我们需要维护一个最右段函数k=2,b=1,和一个分界点集合S={1,1,1}
两个满足条件的函数相加的话,就是最右段函数相加,分界点集合取并。因为都是连续函数,只要维护的斜率是对的,函数值就肯定就是对的。
但是你可以发现维护最右段函数并不重要,分界点集合才重要。所以说最右段函数只是其中一种维护方式。通过几道题来用一下。

CF713C
首先严格递增这个事很烦,把他转化成单调不降,就是令ai=i。然后暴力dp就很显然了。令值域为m
dp(i,j)=mink=1jdp(i1,k)+|aij|
用函数来代替这个dp的第二维fi(x)=dp(i,x)。设函数gi(x)dp(i,j)第二维的前缀最小值对应的函数。那么有
fi(x)=gi(x)+|xai|
考虑用维护最右段函数的方式维护fg。一个下凸函数取前缀最小值的话,相当于是把斜率为0后面的段全部去掉,变成和这段一样。这个h(x)=|xai|是一个很简单的函数,他的最右段函数就是xai,分界点集合就是{ai,ai}。用一个优先队列维护即可,复杂度O(nlogn)
回顾一下刚才的过程,实际上slope trick优化dp大多数就是转移时构造一个gi(x)替代那一坨min,把转移方程转化为对应下标相加的形式。用维护最右段函数来表示这个函数的优势实际上在于这个构造出来的函数如果是前缀最小值的话,非常容易维护。同样的,如果是后缀最小值,可以考虑维护最左段函数。

CF1534G
推性质部分见我的置顶博客去Ctrl+F。快进到优化dp部分。还有,这个dp需要整数定义域是一段连续区间,否则不能维护。那么这个题可以通过倒着设状态来规避dp值里有正无穷的问题。

dp(i,j)=mint=jj+Δidp(i,t)+|jx|

这里的Δiix对于固定的i都是固定的,不用管。第二个号暴力枚举复杂度是对的。
fi(x)=dp(i,j),gi(x)=mint=xx+Δifi(t)
那么有

fi(x)=gi(x)+|xx|

这个gi(x)形式不太好看,由于他不是前缀min也不是后缀min,影响到的东西比较多。但是他是一段包含自己的区间,对于一个下凸函数,x下标函数值变成一段包含自己区间的函数值最小值是很有规律的。我们可以考虑维护中间斜率为0的一段的截距y=b。设中间那段区间为[L,R](分段连续函数开区间闭区间不重要)。
那么对于xL的部分,函数值显然没有变化。对于左边,相当于把这段区间拉长为[LΔi,R]L左边的部分整体平移到LΔi左边。所以用一个大根堆,一个小根堆分别维护两侧的分界点集合,对于一次从fi(x)变成gi(x),把左侧的堆整体打一个标记即可。
往里加一个h(x)=|xv|的话,就要对于v和中间段端点[L,R]大小关系分类讨论了。在区间内的话,直接往左插一个v,往右插一个v。在左边的话,就插入两个v,然后把左边堆顶弹出来插入右边堆即可。过程有点类似对顶堆。右边类似。
这么做是利用凸函数区间min的性质。只要维护出来中间段特殊的地方即可。

APIO2016烟花表演
dp是非常显然的。对于u的每个儿子v,设w(u,v)边权,有dp(u,i)+=mint=0i{dp(v,t)+|w(it)|}
这个形式其实就很复杂了。还是设fu(x)=dp(u,x),gv(x)=mint=0i{fv(t)+|x(w+t)|}
虽然形式很复杂,而且没法把x和枚举min的t拆开。那就整体考虑,但是还是和上一个题一样,这是个凸函数,只要是一段区间的最小值肯定有很好的性质。这是个前缀的最小值,虽然不是f的前缀最小值。。所以说就肯定和中间最低的一段[L,R]有关。具体就不展开了,借助都是整数斜率这个性质,自己分类讨论手推一下容易得到:

gv(x)={fv(x)+wxLfv(L)+L+wxL<xL+wfv(L)L+w<xR+wfv(R)+xRwx>R+w

别管看起来多花里胡哨,别忘了我们只需要维护分界点集合和某一段函数。观察这个形式发现斜率大于0的只有一段,那就可以考虑维护最右段函数。只要能把最右段函数维护出来,前面式子根本不用管,就把分界点弄对就行。仔细观察一下发现fv(R)需要把所有斜率大于0的分界点都弹掉。根据合并的性质,一个函数斜率大于0的段只有儿子个数个,所以暴力弹就对。分界点集合的话,发现就是删掉一个LR,加入一个L+wR+w。然后两个儿子合并的话可以启发式合并就O(nlog2n)。写可并堆就少一个log

posted @   Lebron_Durant  阅读(881)  评论(2编辑  收藏  举报
编辑推荐:
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示