李超线段树

李超线段树

因为太弱了,所以只会用单调队列、CDQ分治、平衡树来维护凸壳,然后被zjp_shadow聚聚在博客底下给D了一顿,所以辣鸡yyb就来学一下了。
(似乎整个机房就我不会了)

首先先明白这个东西在干啥

你要资磁动态维护一个平面直角坐标系,资磁在中间插入一条线段,资磁询问与x=x0这条直线相交的所有线段中,交点的y轴坐标的最大(小)值。

我们要维护的东西是这个:维护这个区间内的所有直线中,从上往下能够看到的最长的那个线段,也就是没有被其他直线覆盖长度最大的段。

考虑怎么插入一条直线,假设它当前处理到了某个区间:

  • 如果这个区间没有记录最长的线段,那么直接把这个区间记录的线段修改为这条线段,然后返回。
  • 如果当前线段在这个区间内已经被这个区间内的最长线段为覆盖,那么直接gg,返回就好了。
  • 反过来,如果完全覆盖了之前记录的线段,那么直接赋值、返回。
  • 否则和已经记录的直线有交,判断哪根线段覆盖的区域较长,把这个区间记录的值给修改一下,然后把短的那一半丢下去递归。

这样子复杂度是啥呢?显然维护复杂度看起来不太对的就是最后一项,但是不难证明每次递归下去直线长度都至少要减少一半,所以这个东西的复杂度就是一个log的。
至于询问?那就是单点询问啦,在线段树上一直走到这个单点为之,把路径上所有记录的线段拿出来取一个max就好啦。

比如BZOJ1568就是模板题QwQ。
当然了,上面这题是每次插入一条直线,这样子只需要一个log,如果每次插入一条线段的话还是要稍微变一下的,即要先找到对应的区间再在这个区间内插入这个线段,这样子复杂度是两个log的,代码戳这里


然后zjp说可以用李超线段树直接维护斜率优化,想了想的确可以,我这里随便搬一道题目过来。

[HNOI2010]玩具装箱

首先写暴力O(n2)的转移,设SiCi的前缀和。

f[i]=minj=0i1f[j]+(ij1+SiSjL)2

f[i]=minj=0i1f[j]+(ij1)2+(SiSjL)2+2(ij1)(SiSjL)

然后把式子拆开,和j无关的直接移出去,只和j相关的放在一起,同时和i,j相关的放在一起。
那么分类之后就是这样的:
j无关的:i22i+1+Si2+L22LSi+2iSi2iL2Si+2L
只和j有关的:f[j]+j2+2j+Sj2+2LSj+2jSj+2jL+2Sj
同时和i,j相关的:2ij2SiSj2iSj2jSi=2(i+Si)(j+Sj)
一共22项,似乎没有什么问题。(其实可以直接令Mi=i1+SiL,但是为了锻炼拆式子能力就这样吧......算了,我编不下去了.....)
那么把和j无关的部分记做pre[i],只和j有关的记做y[j]j+Sj记做x[j]2(i+Si)记做ki
那么转移方程可以改写成:

f[i]=pre[i]+minj=0i1(kix[j]+y[j])

ki是一个常数,我们把后面这个式子理解为一个一次函数y=kx+b的形式,得到b=ykx
什么意思呢?平面上有若干个点(x[j],y[j]),你要过这些点画一条斜率为ki的直线,使得其截距最小。
不难发现满足条件的j一定在下凸壳上。
这里有个很优秀的性质,也就是ki,x[j]都是单增的。
这样子凸壳可以直接用单调队列维护,而取最优值只需要每次找到凸壳左侧最优位置就好啦。

上面是直接用斜率优化,然后单调队列维护凸壳的做法(我直接从别的文章里蒯过来的)
那么我们把那个式子换一下,把y[j]写成b[j]x[j]写成k[j]
那么转移就是

f[i]=pre[i]+minj=0i12(i+Si)k[j]+b[j]

于是问题变成了,平面上有若干条直线,现在你要询问在x=2(i+Si)处的最小值,所以可以直接使用李超线段树来维护。(虽然复杂度多了一个log)。

posted @   小蒟蒻yyb  阅读(4396)  评论(10编辑  收藏  举报
编辑推荐:
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示