李超线段树
李超线段树
引入
题目描述
要求在平面直角坐标系下维护两个操作:
- 在平面上加入一条线段. 记第 \(i\) 条被插入的线段的标号为 \(i\).
- 给定一个数 \(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\) 完整覆盖的线段树区间.
- 如果该区间没有标记, 直接将标记设为 \(f\).
- 若已经有标记, 由于标记难以合并, 只能把标记下传. 但是子节点也有自己的标记, 可能产生冲突, 所以需要递归下传.
如图, 按新线段 \(f\) 的取值是否大于原标记 \(g\), 我们可以把当前区间分为两个子区间. 其中肯定有一个子区间被左区间或右区间完全包含, 换句话说, 在两条线段中, 一定有一条线段, 只可能成为左区间的答案, 或者右区间的答案. 用这条线段递归更新对应的子树, 用另一条线段作为懒标记更新整个区间, 这样就保证了递归下传的复杂度. 当一条线段只可能成为左或右区间的答案时, 才会被下传.
具体来讲, 设当前区间的中点为 \(m\), 我们拿 \(f\) 与 \(g\) 在中点处进行比较.
如果 \(f\) 更优, 则将 \(f\) 与 \(g\) 交换. 现在考虑不优的情况:
- 若在左端点处 \(f\) 更优, 那么 \(f\) 与 \(g\) 必然在左半区间中产生了交点, \(f\) 只会在左区间才可能优于 \(g\), 故递归到左儿子中进行下传.
- 若在右端点处 \(f\) 更优, 那么 \(f\) 与 \(g\) 必然在右半区间中产生了交点, \(f\) 只会在右区间才可能优于 \(g\), 故递归到右儿子中进行下传.
- 否则的话 \(f\) 就不可能成为答案, 返回即可.
除了上述情况外, \(f\) 与 \(g\) 可能恰好交于中点 \(m\), 在实现时可以归入中点处 \(f\) 不如 \(g\) 优的情况, 结果会往 \(f\) 更优的一个端点进行递归下传.
查询
注意懒标记不等价于在区间中点处取值最大的线段.
在查询时, 我们利用标记永久化的思想, 在包含 \(x\) 的所有线段树区间 ( \(\le \log n\) 个) 个标记线段中, 比较得出最终答案.
综上所述, 更新区间的复杂度为 \(\mathcal{O}(n \log^2 n)\), 查询的复杂度为 \(\mathcal{O}(\log n)\).
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现