作用
李超树是拿来维护线段或者直线,支持动态插入线段/直线,和询问在某一单点的最大值线段。
其实就是维护了一个动态凸包。
实际上就是维护了一个“最优势线段”和“标记永久化”。
常常用来维护斜率优化 dp 。
实现
插入直线
首先是插入直线,查询单点的实现:
要记住的地方:
(1.) (Modify) 递归到 (l=r) 的时候可以判断当前单点情况然后更新,直接返回。
(2.) (Modify) 递归到 (tag[x]) 没有值的时候直接更新然后返回。
(3.) (Modify) 更新到当前区间,先比较,然后更新当前的最优势线段,然后递归即可。
(4.) (Modify) 的 (0) 号线段需要重置。
(5.) (Query) 注意要询问沿途经过的所有标记对于当前点的值,因为是标记永久化。
(6.) 如果询问单点的值域太大记得要动态开点。
代码:(维护最小值)
int n,m,top;
int tag[N<<2],k[N],b[N];
#define calc(i,x) (k[i]*x+b[i])
void Modify(int x,int l,int r,int d){
if(l==r){if(calc(d,l)<calc(tag[x],l)) tag[x]=d;return ;}
if(!tag[x]){tag[x]=d;return ;}
int mid=l+r>>1;
int Y1=calc(tag[x],mid),Y2=calc(d,mid);
if(k[tag[x]]<k[d]){
if(Y1<=Y2) Modify(x<<1,l,mid,d);
else Modify(x<<1|1,mid+1,r,tag[x]),tag[x]=d;
}
else if(k[tag[x]]>k[d]){
if(Y1<=Y2) Modify(x<<1|1,mid+1,r,d);
else Modify(x<<1,l,mid,tag[x]),tag[x]=d;
}
else if(b[tag[x]]<b[d]) tag[x]=d;
return ;
}
int Query(int x,int l,int r,int d){
if(l==r) return calc(tag[x],d);
int mid=l+r>>1;int res=calc(tag[x],d);
if(d<=mid) res=min(res,Query(x<<1,l,mid,d));
else res=min(res,Query(x<<1|1,mid+1,r,d));
return res;
}
插入线段
然后是插入线段的操作,具体来说就是要重载一下 (Modify) 函数,只有当我们递归到完全覆盖才进行普通修改。
int n,m,tag[N<<2],tot;
double k[N],b[N];
#define calc(i,x) (k[i]*x+b[i])
void Modify(int x,int l,int r,int d){
if(l==r){if(calc(d,l)>=calc(tag[x],l)) tag[x]=d;return ;}
if(!tag[x]){tag[x]=d;return ;}
int mid=l+r>>1;
double Y1=calc(tag[x],mid),Y2=calc(d,mid);
if(k[tag[x]]<k[d]){
if(Y1<=Y2) Modify(x<<1,l,mid,tag[x]),tag[x]=d;
else Modify(x<<1|1,mid+1,r,d);
}
else if(k[tag[x]]>k[d]){
if(Y1<=Y2) Modify(x<<1|1,mid+1,r,tag[x]),tag[x]=d;
else Modify(x<<1,l,mid,d);
}
else if(b[tag[x]]<=b[d]) tag[x]=d;
return ;
}
void Modify(int x,int l,int r,int ql,int qr,int d){
if(ql<=l&&r<=qr){Modify(x,l,r,d);return ;}
int mid=l+r>>1;
if(ql<=mid) Modify(x<<1,l,mid,ql,qr,d);
if(qr>mid) Modify(x<<1|1,mid+1,r,ql,qr,d);
return ;
}
int Query(int x,int l,int r,int d){
if(l==r) return tag[x];
int mid=l+r>>1;double res=calc(tag[x],d);
if(d<=mid){
int ls=Query(x<<1,l,mid,d);
if(res<calc(ls,d)) return ls;
else if(calc(ls,d)<res) return tag[x];
else return tag[x]>ls?tag[x]:ls;
}
else{
int rs=Query(x<<1|1,mid+1,r,d);
if(res<calc(rs,d)) return rs;
else if(calc(rs,d)<res) return tag[x];
else return tag[x]>rs?tag[x]:rs;
}
return tag[x];
}
李超线段树合并&动态开点李超线段树
和普通线段树合并没有太大区别,主要就是 (Merge) 函数。
代码:
ll k[N],b[N];
int tag[N<<2],ls[N<<2],rs[N<<2],tot;
#define calc(i,x) (k[i]*(x-Base)+b[i])
void Modify(int &x,int l,int r,int d){
if(!x){
x=++cur,tag[x]=d;
return ;
}
if(l==r){if(calc(d,l)<calc(tag[x],l)) tag[x]=d;return ;}
if(!tag[x]){tag[x]=d;return ;}
int mid=l+r>>1;
ll Y1=calc(tag[x],mid),Y2=calc(d,mid);
if(k[tag[x]]<k[d]){
if(Y1<=Y2) Modify(ls[x],l,mid,d);
else Modify(rs[x],mid+1,r,tag[x]),tag[x]=d;
}
else if(k[tag[x]]>k[d]){
if(Y1<=Y2) Modify(rs[x],mid+1,r,d);
else Modify(ls[x],l,mid,tag[x]),tag[x]=d;
}
else if(b[tag[x]]>b[d]) tag[x]=d;
return ;
}
ll Query(int x,int l,int r,int d){
if(!x) return INF;
if(l==r) return calc(tag[x],d);
int mid=l+r>>1;ll res=calc(tag[x],d);
if(d<=mid) res=min(res,Query(ls[x],l,mid,d));
else res=min(res,Query(rs[x],mid+1,r,d));
return res;
}
int Merge(int x,int y,int l,int r){
if(!x||!y) return x|y;
if(l==r) return calc(tag[x],l)>calc(tag[y],l)?y:x;
int mid=l+r>>1;
ls[x]=Merge(ls[x],ls[y],l,mid);
rs[x]=Merge(rs[x],rs[y],mid+1,r);
Modify(x,l,r,tag[y]);
return x;
}