李超线段树学习笔记
李超线段树学习笔记
李超线段树,是一种维护一次函数最值的数据结构,其结构类似于线段树,由大神李超发明,故称之为李超线段树。
前置知识:
1.线段树
2.求两直线交点坐标
代码在这里:
#define N 100500
struct node{
int l,r,id;
}t[N<<2];
#define lc x<<1
#define db double
#define rc x<<1|1
db fabs(db a){
return (a<0)?-a:a;
}
void build(int x,int l,int r){
t[x].l=l,t[x].r=r;
if(l==r)return ;
int mid=l+r>>1;
build(lc,l,mid);build(rc,mid+1,r);
}
int cnt,x1[N],y1[N],x2[N],y2[N];
struct line{
db k,b;
}in[N];
void add(int id){
if(x1[id]==x2[id]){
in[id].k=0;in[id].b=max(y1[id],y2[id]);
return ;
}
in[id].k=1.0*(y1[id]-y2[id])/(x1[id]-x2[id]);
in[id].b=(db)y1[id]-in[id].k*x1[id];
}
李超线段树资瓷2个基本操作:
- 插入一条线段
- 给定整数
,支持查询给定线段中 时的最大纵坐标及其所属线段。
其核心思想是,对于每个区间,它所存储的是优势最大的线段(在最高折线中有最长的一段)。
划分区间的方案类似于线段树。
类比线段树,李超线段树的查询也是对于区间进行的,一次查询就枚举所有的李超线段树里包含
#define pr pair<db,int>
#define mk make_pair
pr fmax(pr a,pr b){
if(fabs(a.first-b.first)<1e-9){
if(a.second>b.second)return b;
return a;
}
return max(a,b);
}
pr find(int x,int k){
pr ans;
int y=t[x].id;
if(t[x].l==t[x].r)return mk(get(y,k),y);
if(y)ans=mk(get(y,k),y);
else ans=mk(0,0);
if(k<=t[lc].r)ans=fmax(ans,find(lc,k));
else ans=fmax(ans,find(rc,k));
return ans;
}
问题在于插入:
下面我们来分类讨论插入线段
- 区间里原本没有线段->直接更新
- 区间原有线段
,对其进行比较:
我们取中点 的值来进行对比,如下图:
就会神秘的发现:
在 处取值更大的线段就是这个区间的最大优势线段
此时我们将最大优势线段更新,如果 优于 ,就执行swap(x,rt)
(此时 就成了新插入的线段)但,这就完了吗?
不一定,新插入的线段即使不是当前区间的最大优势线段,也有可能更新儿子区间。
仍然是之前那张图。容易发现,若
复杂度的话,由于
所以代码实际肥肠简单。
void updata(int x,int k){
int mid=t[x].l+t[x].r>>1;
if(get(t[x].id,mid)<=get(k,mid))swap(k,t[x].id);
if(t[x].l==t[x].r)return ;
if(get(t[x].id,t[x].l)<get(k,t[x].l)||(
get(t[x].id,t[x].l)==get(k,t[x].l)&&t[x].id>k))updata(lc,k);
if(get(t[x].id,t[x].r)<get(k,t[x].r)||(
get(t[x].id,t[x].r)==get(k,t[x].r)&&t[x].id>k))updata(rc,k);//这里是因为题目要求编号最小
}
void change(int x,int l,int r,int k){
if(l<=t[x].l&&t[x].r<=r){
updata(x,k);
return ;
}
int mid=t[x].l+t[x].r>>1;
if(l<=mid)change(lc,l,r,k);
if(mid<r)change(rc,l,r,k);
}
同理,如果是求最小的话反过来就是了。
以模板题HEOI2016线段树为例:
#include<iostream>
#include<cstdio>
using namespace std;
#define N 100500
struct node{
int l,r,id;
}t[N<<2];
#define lc x<<1
#define db double
#define rc x<<1|1
db fabs(db a){
return (a<0)?-a:a;
}
void build(int x,int l,int r){
t[x].l=l,t[x].r=r;
if(l==r)return ;
int mid=l+r>>1;
build(lc,l,mid);build(rc,mid+1,r);
}
int cnt,x1[N],y1[N],x2[N],y2[N];
struct line{
db k,b;
}in[N];
void add(int id){
if(x1[id]==x2[id]){
in[id].k=0;in[id].b=max(y1[id],y2[id]);
return ;
}
in[id].k=1.0*(y1[id]-y2[id])/(x1[id]-x2[id]);
in[id].b=(db)y1[id]-in[id].k*x1[id];
}
db get(int id,int x){
return (db)in[id].k*x+in[id].b;
}
#define pr pair<db,int>
#define mk make_pair
pr fmax(pr a,pr b){
if(fabs(a.first-b.first)<1e-9){
if(a.second>b.second)return b;
return a;
}
return max(a,b);
}
pr find(int x,int k){
pr ans;
int y=t[x].id;//cout<<x<<" "<<t[x].l<<" "<<t[x].r<<" "<<y<<endl;
if(t[x].l==t[x].r)return mk(get(y,k),y);
if(y)ans=mk(get(y,k),y);
else ans=mk(0,0);
if(k<=t[lc].r)ans=fmax(ans,find(lc,k));
else ans=fmax(ans,find(rc,k));
return ans;
}
void updata(int x,int k){
int mid=t[x].l+t[x].r>>1;
if(get(t[x].id,mid)<=get(k,mid))swap(k,t[x].id);
if(t[x].l==t[x].r)return ;
if(get(t[x].id,t[x].l)<get(k,t[x].l)||(
get(t[x].id,t[x].l)==get(k,t[x].l)&&t[x].id>k))updata(lc,k);
if(get(t[x].id,t[x].r)<get(k,t[x].r)||(
get(t[x].id,t[x].r)==get(k,t[x].r)&&t[x].id>k))updata(rc,k);
}
void change(int x,int l,int r,int k){
if(l<=t[x].l&&t[x].r<=r){
updata(x,k);
return ;
}
int mid=t[x].l+t[x].r>>1;
if(l<=mid)change(lc,l,r,k);
if(mid<r)change(rc,l,r,k);
}
int lst,n;
int main(){
// freopen("data.in","r",stdin);
// freopen("data.out","w",stdout);
ios::sync_with_stdio(false);
cin>>n;
build(1,1,40000);
for(int i=1;i<=n;i++){
int op;cin>>op;
if(!op){
int k;cin>>k;k=(k+lst+39988)%39989+1;
lst=find(1,k).second;
cout<<lst<<"\n";
}
else {
swap(++cnt,i);
cin>>x1[i]>>y1[i]>>x2[i]>>y2[i];
x1[i]=(x1[i]+lst+39988)%39989+1;
x2[i]=(x2[i]+lst+39988)%39989+1;
y1[i]=(y1[i]+lst+999999999)%1000000000+1;
y2[i]=(y2[i]+lst+999999999)%1000000000+1;
if(x1[i]>x2[i]){
swap(x1[i],x2[i]);
swap(y1[i],y2[i]);
}
add(i);
// cout<<in[i].k<<" "<<in[i].b<<endl;
change(1,x1[i],x2[i],i);
swap(i,cnt);
}
}
}
而因为李超线段树的特性,也被用于斜率优化,主要是将斜率式转化为点斜式方程虽然李超不见得多好写.
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战