李超线段树
李超线段树
因为太弱了,所以只会用单调队列、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\))。