队爷的智慧
李超线段树
问题引入
顾名思义,这是由队爷lc提出的一种线段树,用于维护一下问题:
在平面直角坐标系下维护两个操作:
- 在平面上加入一条线段
- 查询直线 $x = k$ 与已有线段交点的纵坐标最大的线段的编号(也可以得到最大值)
算法流程
既然是线段树,自然少不了区间和区间信息
lc线段树以值域为区间,对于区间 $[l, r]$ lc线段树维护的信息是“与直线 $x = mid$ 的交点纵坐标最大的线段”
询问很好解决了,只要对所有包含 $k$ 的区间上的线段都计算一下即可
问题在于跟新,如何在加入一条线段后维护出新的最大线段,考虑向节点信息为 $f_1(x) = k_1x + b$ 的区间 $[l, r]$ 插入一条线段 $f_2(x) = k_2x + b$ (保证线段可以覆盖整个区间),分类讨论(下面红色代表 $f_1$ ,蓝色代表 $f_2$ ,绿色代表 $x = mid$ ):
两条线段有明确的大小关系,即在区间内一条线段完全大于(小于)另一条,直接跟新即可
$k_2 > k_1 \wedge f_1(mid) < f_2(mid)$
此时用 $f_2$ 跟新当前区间,并用 $f_1$ 跟新左子树(这里有一个类似标记永久化的思想,右子树的信息可能并不是该有的 $f_2$ ,但询问时我们会用 $f_2$ 的答案而不会取右子树的答案)
- $k_2 < k_1 \wedge f_1(mid) < f_2(mid)$
此时用 $f_2$ 跟新当前区间,并用 $f_1$ 跟新右子树
- $k_2 > k_1 \wedge f_1(mid) > f_2(mid)$
此时用 $f_2$ 跟新右子树,当前区间不变
- $k_2 < k_1 \wedge f_1(mid) > f_2(mid)$
(懒得放图了)
此时用 $f_2$ 跟新左子树,当前区间不变
lc线段树没有 push_up
和 push_down
有一个需要注意的点是,为了方便求值和交点,我们用斜截式存线段,但垂直于 $x$ 轴的直线没有斜截式
我们考虑到对于垂直于 $x$ 轴的线段 $(x_0, y_1) \to (x_0, y_2), (y1 \le y2)$ ,在只关注最值的情况下,它等效于一个点 $(x_0, y_2)$ ,而点就可以写成 $y = 0x + y_2, (x_0 \le x \le x_0)$ (就是平行于 $x$ 轴的,长度只有一个点的直线)
考虑时间:
询问操作要遍历每一个包含 $k$ 的区间,是 $O(\log n)$ 的,而插入线段操作首先要把这个线段分给若干区间(保证线段一定覆盖区间),这是一个类似区间查询的操作,要 $O(\log n)$ ,而对于每个区间,我们会递归跟新它的儿子,又要一个 $O(\log n)$ 所以插入操作的总时间是 $O(\log^2 n)$ 的
注意上面的 $n$ 指的是值域
代码
1 |
|