李超线段树
因为太弱了,所以只会用单调队列、CDQ分治、平衡树来维护凸壳,然后被(zjp\_shadow)聚聚在博客底下给D了一顿,所以辣鸡yyb就来学一下了。
(似乎整个机房就我不会了)
首先先明白这个东西在干啥
你要资磁动态维护一个平面直角坐标系,资磁在中间插入一条线段,资磁询问与(x=x0)这条直线相交的所有线段中,交点的(y)轴坐标的最大(小)值。
我们要维护的东西是这个:维护这个区间内的所有直线中,从上往下能够看到的最长的那个线段,也就是没有被其他直线覆盖长度最大的段。
考虑怎么插入一条直线,假设它当前处理到了某个区间:
- 如果这个区间没有记录最长的线段,那么直接把这个区间记录的线段修改为这条线段,然后返回。
- 如果当前线段在这个区间内已经被这个区间内的最长线段为覆盖,那么直接(gg),返回就好了。
- 反过来,如果完全覆盖了之前记录的线段,那么直接赋值、返回。
- 否则和已经记录的直线有交,判断哪根线段覆盖的区域较长,把这个区间记录的值给修改一下,然后把短的那一半丢下去递归。
这样子复杂度是啥呢?显然维护复杂度看起来不太对的就是最后一项,但是不难证明每次递归下去直线长度都至少要减少一半,所以这个东西的复杂度就是一个(log)的。
至于询问?那就是单点询问啦,在线段树上一直走到这个单点为之,把路径上所有记录的线段拿出来取一个(max)就好啦。
比如BZOJ1568就是模板题QwQ。
当然了,上面这题是每次插入一条直线,这样子只需要一个(log),如果每次插入一条线段的话还是要稍微变一下的,即要先找到对应的区间再在这个区间内插入这个线段,这样子复杂度是两个(log)的,代码戳这里。
然后(zjp)说可以用李超线段树直接维护斜率优化,想了想的确可以,我这里随便搬一道题目过来。
[HNOI2010]玩具装箱
首先写暴力(O(n^2))的转移,设(S_i)是(C_i)的前缀和。
然后把式子拆开,和(j)无关的直接移出去,只和(j)相关的放在一起,同时和(i,j)相关的放在一起。
那么分类之后就是这样的:
和(j)无关的:(i^2-2i+1+S_i^2+L^2-2LS_i+2iS_i-2iL-2S_i+2L)
只和(j)有关的:(f[j]+j^2+2j+S_{j}^2+2LS_j+2jS_j+2jL+2S_j)
同时和(i,j)相关的:(-2ij-2S_iS_j-2iS_j-2jS_i=-2(i+S_i)(j+S_j))
一共(22)项,似乎没有什么问题。(其实可以直接令(M_i=i-1+S_i-L),但是为了锻炼拆式子能力就这样吧......算了,我编不下去了.....)
那么把和(j)无关的部分记做(pre[i]),只和(j)有关的记做(y[j]),(j+S_j)记做(x[j]),(2(i+S_i))记做(k_i)。
那么转移方程可以改写成:
而(k_i)是一个常数,我们把后面这个式子理解为一个一次函数(y=kx+b)的形式,得到(b=y-kx)。
什么意思呢?平面上有若干个点((x[j],y[j])),你要过这些点画一条斜率为(k_i)的直线,使得其截距最小。
不难发现满足条件的(j)一定在下凸壳上。
这里有个很优秀的性质,也就是(k_i,x[j])都是单增的。
这样子凸壳可以直接用单调队列维护,而取最优值只需要每次找到凸壳左侧最优位置就好啦。
上面是直接用斜率优化,然后单调队列维护凸壳的做法(我直接从别的文章里蒯过来的)
那么我们把那个式子换一下,把(y[j])写成(b[j]),(-x[j])写成(k[j]),
那么转移就是
于是问题变成了,平面上有若干条直线,现在你要询问在(x=2(i+S_i))处的最小值,所以可以直接使用李超线段树来维护。(虽然复杂度多了一个(log))。