李超线段树

李超线段树

引入

【模板】李超线段树

题目描述

要求在平面直角坐标系下维护两个操作:

  1. 在平面上加入一条线段. 记第 \(i\) 条被插入的线段的标号为 \(i\).
  2. 给定一个数 \(k\), 询问与直线 \(x = k\) 相交的线段中, 交点纵坐标最大的线段的编号.

强制在线.

我们发现, 传统线段树无法很好地维护这样的信息. 在这种情况下, 李超线段树便应运而生.

用处

插入直线 / 线段, 单点查询极值.

过程

我们可以将任务转化成维护以下操作:

  • 每次加入一个一次函数, 定义域 \([l, r]\).
  • 给定 \(k\), 求定义域包含 \(k\) 的所有一次函数中, 在 \(x = k\)\(y\) 值最大的一个, 如果有多个相同的, 取编号最小的.

当线段垂直于 \(x\) 轴时, 假设线段两端为 \((x, y_0), (x, y_1)\), \(y_0 < y_1\), 则插入定义域为 \([x, x]\) 的一次函数 \(y = 0 \cdot x + y1\)​.

修改

看到区间修改, 按照线段树惯用的解决方法, 每个节点打一个懒标记. 每个节点 \(i\) 的懒标记是一条线段, 记为 \(l_i\), 表示要用 \(l_i\) 更新该节点所表示的整个区间.

假设现在我们要插入一条线段 \(f\), 考虑某个被 \(f\) 完整覆盖的线段树区间.

  1. 如果该区间没有标记, 直接将标记设为 \(f\).
  2. 若已经有标记, 由于标记难以合并, 只能把标记下传. 但是子节点也有自己的标记, 可能产生冲突, 所以需要递归下传.

li-chao-tree-1.webp

如图, 按新线段 \(f\) 的取值是否大于原标记 \(g\), 我们可以把当前区间分为两个子区间. 其中肯定有一个子区间被左区间或右区间完全包含, 换句话说, 在两条线段中, 一定有一条线段, 只可能成为左区间的答案, 或者右区间的答案. 用这条线段递归更新对应的子树, 用另一条线段作为懒标记更新整个区间, 这样就保证了递归下传的复杂度. 当一条线段只可能成为左或右区间的答案时, 才会被下传.

具体来讲, 设当前区间的中点为 \(m\), 我们拿 \(f\)\(g\) 在中点处进行比较.

如果 \(f\) 更优, 则将 \(f\)\(g\) 交换. 现在考虑不优的情况:

  1. 若在左端点处 \(f\) 更优, 那么 \(f\)\(g\) 必然在左半区间中产生了交点, \(f\) 只会在左区间才可能优于 \(g\), 故递归到左儿子中进行下传.
  2. 若在右端点处 \(f\) 更优, 那么 \(f\)\(g\) 必然在右半区间中产生了交点, \(f\) 只会在右区间才可能优于 \(g\), 故递归到右儿子中进行下传.
  3. 否则的话 \(f\) 就不可能成为答案, 返回即可.

除了上述情况外, \(f\)\(g\) 可能恰好交于中点 \(m\), 在实现时可以归入中点处 \(f\) 不如 \(g\) 优的情况, 结果会往 \(f\) 更优的一个端点进行递归下传.

查询

注意懒标记不等价于在区间中点处取值最大的线段.

在查询时, 我们利用标记永久化的思想, 在包含 \(x\) 的所有线段树区间 ( \(\le \log n\) 个) 个标记线段中, 比较得出最终答案.

综上所述, 更新区间的复杂度为 \(\mathcal{O}(n \log^2 n)\), 查询的复杂度为 \(\mathcal{O}(\log n)\).

posted @   Steven1013  阅读(3)  评论(0编辑  收藏  举报
编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示