zoukankan      html  css  js  c++  java
  • 李超线段树 复习

    作用

    李超树是拿来维护线段或者直线,支持动态插入线段/直线,和询问在某一单点的最大值线段。

    其实就是维护了一个动态凸包。

    实际上就是维护了一个“最优势线段”和“标记永久化”。

    常常用来维护斜率优化 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;
    }
    

    例题

    P4254 [JSOI2008]Blue Mary开公司

    P4097 [HEOI2013]Segment

    P3195 [HNOI2008]玩具装箱

    P4069 [SDOI2016]游戏

    P6047 丝之割

    CF1303G Sum of Prefix Sums

    CF932F Escape Through Leaf

    P2900 [USACO08MAR]Land Acquisition G

    P3628 [APIO2010]特别行动队

  • 相关阅读:
    使用Python从Workflowy同步大纲到印象笔记
    使用Jenkins自动部署博客
    使用有限状态机原理实现英文分词
    TeamFlowy——结合Teambition与Workflowy提高生产力
    技巧收集-M1709
    Tenacity——Exception Retry 从此无比简单
    记住 Python 变量类型的三种方式
    Python 装饰器装饰类中的方法
    技巧收集-W1701
    DotnetSpider爬虫简单示例 net core
  • 原文地址:https://www.cnblogs.com/Akmaey/p/14677431.html
Copyright © 2011-2022 走看看