李超线段树

主要内容

李超线段树可以用来维护平面上的线段(但是要求 \(x\)\(y\) 其中一维比较小,在 \(10^5\) 及以内)。

李超树核心是记下每一段区间的最优线段

一条线段能成为区间 \([l,r]\) 中的最优线段,当且仅当:

  • 该线段的定义域完整覆盖了区间 \([l,r]\)
  • 该线段在区间中点处最优。

只用在普通线段树树上再加上一只 \(\log\) 就好了。

经典性质:

  • 在动态开点李超树中,一条线段只会被至多一个定点记录(每次被替换后才可能被加入,不会重复加)。

模板题:P4097 [HEOI2013]Segment

给定平面上的一些线段,每次询问求出当前与 \(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

例题:P4655 [CEOI2017]Building Bridges

posted @ 2021-10-26 16:55  EricQian06  阅读(74)  评论(0编辑  收藏  举报