李超线段树
动态添加线段,求某个 \(x\) 对应的 \(y\) 最大是多少,且对应哪条直线。
因为 \(x\) 比较小,考虑在 \(x\) 轴上建立线段树。把每个线段写成 \(y=kx+b\) 的解析式形式并求出它的定义域 \([l,r]\),每条线段就可以看作是一个应用在 \([l,r]\) 上的区间修改。每次查询就是单点查询。
因为是单点查询,我们可以考虑标记永久化的做法:尝试让每一条线段分成若干个结点后,永久停留在那个结点上。
但是这可能有一个问题:如果我们准备一个结点上打标记,但是这里已经有一个标记了,怎么办?
肯定不能用 vector 存所有标记,复杂度会爆。
首先,如果在这个结点代表的区间内,一条线段严格优于另一条线段,可以直接替换。
否则,我们采取标记下传的方式。
关键性质:两条定义域为 \([l,r]\) 的线段,在 \(m=\dfrac{l+r}{2}\) 的左右两边,肯定有一条线段严格优于另一条线段。
假设我们新加的线段是 \(s_1\),原本在这个结点的 tag 处的线段是 \(s_2\)。考虑 \(m\) 左右两边,如果是 \(s_1\) 有一段严格优于 \(s_2\),交换 \(s_1,s_2\)。
于是我们只需要考虑 \(s_2\) 有一半严格优于 \(s_1\) 的情况了。
找出 \(s_2\) 不严格优于 \(s_1\) 的那一半是左儿子还是右儿子,把 \(s_1\) 递归下传到对应的儿子里面。
这样的复杂度是多少?
加入一条线段,会拆分为 \(O(\log n)\) 个结点,每个结点上会打标记,每次打标记最多可能会沿着一个儿子下传 \(O(\log n)\) 层。所以一次修改的复杂度是 \(O(\log^2n)\) 的。
一次查询只需要把根到叶子的所有结点上保存的所有线段取出来比较即可。\(O(\log n)\)。
李超线段树只能维护直线吗?
其实李超线段树复杂度的保证是那个关键性质。所以如果两个函数图像也满足分成两半后,有一半是能直接决定出来的,也可以用李超线段树做。(比如半圆,反比例函数,甚至反比例函数和直线结合)
同时,抛物线也可以!(应该吧,下面这段不太确定)
虽然抛物线可能有两个交点,也不一定有一半能直接决定出来,但是我们把一个抛物线沿着它的最低点剖成两半:一个单调递减的抛物线和一个单调递增的抛物线。
发现单调递减的抛物线和单调递增的抛物线之间的交点也都只会有一个。
这里有一个想法:其实李超线段树可以维护任意同一次数多项式的图像。因为线段能维护的原因是两个线段的解析式做差后得到的还是一个一元一次方程,抛物线做差也还是一个二次方程。
李超线段树还可以用来做斜率优化 DP 的推广。
斜率优化的式子:\(dp[i]=\max(k(j)x(i)+b(j))\),虽然是 \(O(n)\) 的,但是对 \(k\) 和 \(x\) 有单调性要求。但是李超线段树没有这个要求。

浙公网安备 33010602011771号