学习了新的斜率优化。
斜率优化除了一般形式,还有另一个形式:
$f_{i}=min(k*x+b)+v$
其中k是个和x有关的常数,v是个常数,b是个和y有关的常数
把k看做斜率,b看做截距,问题就是找一个j让这个直线的函数值最小,就是找一条平行于y轴的直线让函数值最小。
是个李超树/动态半平面交的模板题。
很多斜率优化都能规约到这个问题。
如$f_{i}=min((a_{i}-a_{j})^2+f_{j})$
拆开括号,$f_{i}=min(-(2*a_{j})*a_{i}+(a_{j}^2+f_{j}))+a_{i}^2$
实际上,把k=-2*a[j],x=a[i],b=a[j]^2+f[j],就是这个问题。
回到本题。这道题的dp方程$f[i]=min(max(t[j+1]...t[i])suf[i+1]+f[j]) j=l[i]~i-1$就是个斜率优化的形式。
考虑cdq分治。考虑[l,md]对[md+1,r]的贡献。
枚举[md+1,r]的节点i,设[md+1,i]的最大t值为v
找到一个位置p,使得[p,md]的最大t值<=v而[p-1,md]>v
[p,md]的部分中,对f的贡献变为f[i]=min(f[j])+v*suf[i+1],维护f的后缀最小值直接计算。
[max(l,l[i]),p-1]中的斜率不受影响,可以直接预处理然后使用线段树查询。
这题还能用树剖做。
直接考虑维护max的过程。实际上在数据随机的时候,维护一个单调栈,单调栈的相邻两个数所代表的区间[s[i]+1,s[i+1]]的max值是相同的,记为val。
dp方程变成了f[i]=min(val*suf[i+1]+f[j])。
对于一个区间维护一条直线k=val,b=f[j],则在对应区间建立线段树即可查询。
但是线段树在弹栈的时候的直线斜率会修改,十分麻烦。
考虑一种不会破坏线段树的方法。
如果在弹栈的时候把相邻两个栈的节点连边,最后把单调栈的相邻元素连边,则形成了一棵树。
这棵树的性质是:一个点跳到根节点遇到的点就是要查询的。一个点跳到根的t是递增的。
对于一个点维护直线$y=t_xx+min{f_{fa_x+1cdots x}}$。则每一次就是对到根节点的所有点查询s[i+1],是单调递减的。
树剖+线段树维护。