【学习笔记】李超线段树
维护一次函数#
以 模板题 为例。
使用线段树维护线段,每个节点维护的都是完全覆盖这个区间的线段。
考虑当前节点已经有线段 ,现在加入线段 。
暴力想法是暴力递归每个子区间,把更优的保留,注意到 最多一个交点,因此也最多一侧的子区间需要暴力递归。
具体流程如下:
先比较 处点值,钦定 为 处点值不劣的线段。
-
若 处点值 更优,说明交点在左区间,那么右区间更优的仍为 ,左区间递归处理。
-
若 处点值 更优,说明交点在右区间,那么左区间更优的仍未 ,右区间递归处理。
-
特别地,若 或 处点值相等,且新增加的线段在 处点值更优,那么就递归处理对应的区间。
-
处理之后保留在当前节点的线段应当为 ,即 处点值不劣的线段。
这样类似于标记永久化,查询时递归每个区间,总复杂度 。(如果添加直线就是 。)
点击查看代码
int n,X=39989,Y=1000000000;
struct Line{
db k,b;
Line()=default;
Line(db k_,db b_):k(k_),b(b_){}
}L[maxn];
int tot;
inline db get_y(int id,int x){
if(!id) return 0;
return L[id].k*x+L[id].b;
}
inline int check(int id1,int id2,int x){
db y1=get_y(id1,x),y2=get_y(id2,x);
if(y1-y2>eps) return 1;
else if(y2-y1>eps) return -1;
else return 0;
}
inline pdi max(pdi A,pdi B){
if(A.fir-B.fir>eps) return A;
else if(B.fir-A.fir>eps) return B;
else{
if(A.sec<B.sec) return A;
else return B;
}
}
struct SegmentTree{
#define mid ((l+r)>>1)
#define lson rt<<1,l,mid
#define rson rt<<1|1,mid+1,r
int tag[maxn<<2];
void update(int rt,int l,int r,int id){
if(!tag[rt]) return tag[rt]=id,void();
if(check(id,tag[rt],mid)==1) swap(tag[rt],id);
int chkl=check(id,tag[rt],l),chkr=check(id,tag[rt],r);
if(chkl==1||(!chkl&&id<tag[rt])) update(lson,id);
if(chkr==1||(!chkr&&id<tag[rt])) update(rson,id);
}
void insert(int rt,int l,int r,int pl,int pr,int id){
if(pl<=l&&r<=pr){
update(rt,l,r,id);
return;
}
if(pl<=mid) insert(lson,pl,pr,id);
if(pr>mid) insert(rson,pl,pr,id);
}
pdi query(int rt,int l,int r,int x){
db y=get_y(tag[rt],x);
pdi res=make_pair(y,tag[rt]);
if(l==r) return res;
if(x<=mid) res=max(res,query(lson,x));
else res=max(res,query(rson,x));
return res;
}
#undef mid
#undef lson
#undef rson
}S;
int lastans;
int main(){
n=read();
while(n--){
int opt=read();
if(opt==1){
int x0=read(),y0=read(),x1=read(),y1=read();
x0=(x0+lastans-1)%X+1,y0=(y0+lastans-1)%Y+1,x1=(x1+lastans-1)%X+1,y1=(y1+lastans-1)%Y+1;
if(x0>x1) swap(x0,x1),swap(y0,y1);
if(x0==x1) L[++tot]=Line(0,max(y0,y1));
else{
db k=1.0*(y1-y0)/(x1-x0),b=y0-k*x0;
L[++tot]=Line(k,b);
}
S.insert(1,1,X,x0,x1,tot);
}
else{
int x=read();
x=(x+lastans-1)%X+1;
lastans=S.query(1,1,X,x).sec;
printf("%d\n",lastans);
}
}
return 0;
}
斜率优化 DP#
斜率优化 DP 中,通常写成 的形式,其中 均与决策点 有关,通常是使用数据结构维护凸包。
在 具有单调性的前提下, 具有单调性的情况,可以单调队列维护;反之可以单调栈维护并二分。
当 不具有单调性时,就需要用到李超线段树。
直接抛弃前面对凸包的依赖,我们所要找的实际上过 的且斜率为 直线中,截距满足最值要求的一个。直接移项成 ,这样就是把 代入求最值了,可以李超线段树维护。
因此李超线段树可以解决没有任何单调性的斜率优化。
例题#
Luogu-P4254 JSOI 2008 Blue Mary 开公司#
维护一次函数模板。
Luogu-P2497 SDOI 2012 基站建设#
运用几何知识可以得到:。
转移方程:
移项后意识到 ,李超线段树维护。(离散化或动态开点均可)
参考资料#
-
OI Wiki
作者:SoyTony
出处:https://www.cnblogs.com/SoyTony/p/Learning_Notes_about_Li-Chao_Segment_Tree.html
版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效