李超线段树
主要内容
李超线段树可以用来维护平面上的线段(但是要求 \(x\) 或 \(y\) 其中一维比较小,在 \(10^5\) 及以内)。
李超树核心是记下每一段区间的最优线段:
一条线段能成为区间 \([l,r]\) 中的最优线段,当且仅当:
- 该线段的定义域完整覆盖了区间 \([l,r]\) ;
- 该线段在区间中点处最优。
只用在普通线段树树上再加上一只 \(\log\) 就好了。
经典性质:
- 在动态开点李超树中,一条线段只会被至多一个定点记录(每次被替换后才可能被加入,不会重复加)。
给定平面上的一些线段,每次询问求出当前与 \(x=k\) 相交的线段中 \(y\) 值最大的线段编号。
struct Line { double b,k; }a[Maxn];
inline double calc(int p,int x){ return a[p].b+a[p].k*1.0*x; }
inline int tomax(int p1,int p2,int x)
{ return (calc(p1,x)>calc(p2,x)+eps)?p1:p2; }
struct Segment_Tree
{
int tree[Maxn<<2];
void add(int p,int nl,int nr,int l,int r,int x)
{
int mid=(nl+nr)>>1;
if(nl>=l && nr<=r)
{
if(calc(x,mid)>calc(tree[p],mid)+eps) swap(tree[p],x);
if(calc(x,nl)>calc(tree[p],nl)+eps) add(p<<1,nl,mid,l,r,x);
if(calc(x,nr)>calc(tree[p],nr)+eps) add(p<<1|1,mid+1,nr,l,r,x);
return;
}
if(mid>=l) add(p<<1,nl,mid,l,r,x);
if(mid<r) add(p<<1|1,mid+1,nr,l,r,x);
}
int query(int p,int nl,int nr,int x)
{
if(nl==nr) return tree[p];
int mid=(nl+nr)>>1;
if(mid>=x) return tomax(tree[p],query(p<<1,nl,mid,x),x);
else return tomax(tree[p],query(p<<1|1,mid+1,nr,x),x);
}
}T;
李超线段树的合并
前置知识:线段树合并、分裂。
本质上和线段树没什么区别,只是在 merge
完两棵子树后的 update
中改为将 \(y\) 的最优线段再放到 \(x\) 中进行一次 add
即可。
int merge(int x,int y,int nl,int nr)
{
if(!x || !y) return x+y;
int mid=(nl+nr)>>1;
tree[x].pl=merge(tree[x].pl,tree[y].pl,nl,mid);
tree[x].pr=merge(tree[x].pr,tree[y].pr,mid+1,nr);
add(x,nl,nr,tree[y].num);
return x;
}
例题:CF932F Escape Through Leaf。
李超线段树优化 dp
李超线段树还可以用来维护斜率优化的问题,详见斜率优化DP;