李超线段树 学习笔记
李超线段树 学习笔记
引入
最近一直在做斜率优化的题,然而只会傻傻维护凸包,一到横坐标不单调,就涉及到手打平衡树,但是我实在不想学平衡树了,所以就准备掏出解决处理直线的大宝贝——李超线段树
功能
有两种操作:
插入一条表达式为
给出
实现
原理
维护区间的中点对应的最大值的直线,用到了标记永久化的思想
维护方法
修改
(偷了两张CSDN博主的图)
如当前情况,蓝色的直线是我们加入的直线,如果它比这个区间所有的都大(完全覆盖),那么就把原有线段换成这条直线;反之则直接舍弃。
下一种情况就是不完全覆盖(有交点)
-
首先对于当前这个大区间,现在应该取
更大的直线作为当前区间的标记,那么对于整个区间都是如此吗? -
No,可以看到两条直线在
之间是存在交点的, 那么在左区间的某个位置, 的大小关系就可能发生变化,所以我们需要递归进入有交点的区间来进一步修改
查询
和标记永久化的线段树查询一样,我们查询一个位于
我们要把橙色、绿色、蓝色、黄色区间的直线之间所有的最大值都取到。
例题
分析
例题用Build Bridges作为板子,本题写出来的状态转移方程是:
按照正常斜率优化的套路写出来的话,原式就变成了:
这时候你发现我们要作为斜率的
可以发现前面是常数项,后面很像一个直线的形式了,我们可以看成给出一个横坐标,求
其中我们每给出一条直线,就看成插入
注意一开始要让
Code
注意
写斜率优化的时候完全不用考虑直线的左右端点,直接插入和查询就是了,因为你插入的这个直线一定是无限长的,所以可以把所有的横坐标都囊括,因为我们用当前线段去更新这个区间的前提,就是这个区间被完全包括在这个线段之内。
但是如果插入直线的时候并不是无限长的,我们就要先按照定义找到被完全包含于这条直线的线段区间,然后再对它以及它的子线段递归进行更新。
普通李超线段树的插入写出来就是这样的:
struct SegmentTree{int idx,l,r;}rt[x]; int k[maxn],b[maxn]; int calc(int x,int idx){return 1ll*k[idx]+b[idx];} inline void modify(int x,int l,int r,int idx) { int mid=rt[x].l+rt[x].r>>1; if(l<=rt[x].l&&rt[x].r<=r) { if(calc(mid,rt[x].idx)>calc(mid,idx))rt[x].idx=idx; modify(x<<1,l,r,idx),modify(x<<1|1,l,r,idx); } if(rt[x].l==rt[x].r) { if(calc(mid,rt[x].idx)>calc(mid,idx))rt[x].idx=idx; return ; } if(l<=mid)modify(x<<1,l,r,idx); if(r>mid)modify(x<<1|1,l,r,idx); }
复杂度
综上,斜率优化写的李超树只是李超线段树的一种特殊情况(每条直线的端点就都是整个区间的左右端点)。
我们在写普通李超线段树的插入操作的时候,把区间分割成一个个能被完全包含的小区间的复杂度是
插入:
查询:
本文来自博客园,作者:Hanggoash,转载请注明原文链接:https://www.cnblogs.com/Hanggoash/p/16829796.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效