李超线段树

李超线段树

用途:给定 $\mathrm{n}$ 个 $\mathrm{y}=\mathrm{kx}+\mathrm{b}$ 形式的线段,问 $\mathrm{x}=\mathrm{x[0]}$ 与哪条线段交点的纵坐标值最大.

对于李超树的每一个区间,维护这条区间的 “优势线段”,即区间 $\mathrm{mid}$ 位置上纵坐标最大的线段.  

当在区间中插入一条线段时,按照下列步骤:

1. 若新插入线段中点位置大于当前线段,则该区间的优势线段更新为新线段,并将当前线段向下递归更新.

2. 若将递归更新的线段左/右端点的值小于优势线段,显然会完全被覆盖掉,就没有必要继续递归了.  

3. 若满足递归条件,则递归继续更新.  

插入线段时间复杂度为 $O(n \log ^2 n)$,查询复杂度为 $O(n \log n)$.   

Segment

来源:洛谷P4097 [HEOI2013]Segment

模板题,注意线段的 $\mathrm{x[0]}=\mathrm{x[1]}$ 时将斜率取为 $0$ 即可.   

#include <cstdio>
#include <cstring> 
#include <cmath>
#include <vector> 
#include <set>
#include <algorithm>
#define M  50003
#define N  100009 
#define ll long long 
#define pb push_back 
#define ls (now<<1) 
#define rs (now<<1|1)    
#define setIO(s) freopen(s".in","r",stdin)    
using namespace std; 
const int mod=39989; 
const double eps=1e-8;
int tree[N<<2];   
struct seg {
    double k,b;   
    seg(double i=0.0,double j=0.0) { k=i,b=j;}    
}s[N];  
double calc(seg o, int x) {
    return o.k*x+o.b;  
}    
void modify(int l,int r,int now,int L,int R,int x) {
    int mid=(l+r)>>1;  
    if(l>=L&&r<=R) {
        if(calc(s[x],mid)-calc(s[tree[now]],mid)>eps) swap(x, tree[now]);    
        if(calc(s[x],l)-calc(s[tree[now]],l)>eps) 
            modify(l,mid,ls,L,R,x); 
        if(calc(s[x],r)-calc(s[tree[now]],r)>eps) 
            modify(mid+1,r,rs,L,R,x);  
        return ; 
    }
    if(L<=mid) modify(l,mid,ls,L,R,x); 
    if(R>mid)  modify(mid+1,r,rs,L,R,x);  
}    
void upd(int &x,int o,int p) {
    double yx=s[x].k*p+s[x].b;  
    double yo=s[o].k*p+s[o].b;   
    if(yo-yx>=eps||(abs(yo-yx)<=eps&&o<x)) x=o;    
}
int query(int l,int r,int now,int p) {
    if(l==r) {
        return tree[now]; 
    }
    int mid=(l+r)>>1,id=tree[now];     
    if(p<=mid)  upd(id, query(l,mid,ls,p), p);  
    else upd(id, query(mid+1,r,rs,p), p);  
    return id;         
}
int main() { 
    // setIO("input");      
    // freopen("input.out","w",stdout);   
    int Q,lastans=0,n=0;  
    scanf("%d",&Q); 
    while(Q--) {
        int op; 
        scanf("%d",&op);  
        if(op==0) {
            int k,x; 
            scanf("%d",&k);  
            k=(k+lastans-1)%mod+1;  
            printf("%d\n",lastans=query(1,M,1,k));    
        }
        else {
            int x[2],y[2];  
            scanf("%d%d%d%d",&x[0],&y[0],&x[1],&y[1]);  
            x[0]=(x[0]+lastans-1)%mod+1;  
            y[0]=(y[0]+lastans-1)%1000000000+1;    
            x[1]=(x[1]+lastans-1)%mod+1; 
            y[1]=(y[1]+lastans-1)%1000000000+1;                            
            if(x[0]==x[1]) {
                s[++n].k=0,s[n].b=max(y[0], y[1]);    
            }
            else {
                s[++n].k=(double)1.0*(y[1]-y[0])/(x[1]-x[0]);  
                s[n].b=y[0]-1.0*s[n].k*x[0];     
            }
            if(x[0]>x[1]) swap(x[0],x[1]); 
            modify(1,M,1,x[0],x[1],n);  
        }
    }
    return 0; 
}

  

游戏

来源:洛谷P4069 [SDOI2016]游戏 

树链剖分套李超线段树,时间复杂度为 $O(n \log ^3 n)$,不过树剖和李超树的常数都很小.   

将这个路径加数写成对于点 $\mathrm{i}$ 只关于 $\mathrm{dis[i]}$ 的式子后发现是 $\mathrm{ax+b}$ 的形式.  

本题在模板的基础上再维护一个区间极小值就行,然后路径修改时对于 $\mathrm{lca}$ 两侧分类讨论一下.   

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector> 
#define N 100008 
#define ls now<<1 
#define rs now<<1|1
#define ll long long 
#define pb push_back 
#define setIO(s) freopen(s".in","r",stdin) 
using namespace std;             
const ll inf=123456789123456789ll;      
struct seg {
    ll k,b;  
    seg() { k=b=0; }  
}s[N];          
ll mi[N<<2],pl[N<<2],pr[N<<2],dis[N];     
int tree[N<<2],idx[N<<2],dep[N],n,m,edges,cn;     
int hd[N],to[N<<1],nex[N<<1],val[N<<1],fa[N],size[N],son[N],top[N],dfn[N],scc; 
ll calc(seg o, ll pos) {
    return o.k*pos+o.b;    
}   
ll get(int now) {
    if(!tree[now]) return inf; 
    else {  
        return min(calc(s[tree[now]], pl[now]), calc(s[tree[now]], pr[now]));       
    }
}
void pushup(int now) {    
    mi[now]=min(get(now), min(mi[ls], mi[rs]));   
}
void build(int l,int r,int now) {
    mi[now]=inf;     
    pl[now]=dis[idx[l]]; 
    pr[now]=dis[idx[r]];       
    if(l==r) return ;  
    int mid=(l+r)>>1;  
    build(l,mid,ls); 
    build(mid+1,r,rs); 
}
ll query(int l,int r,int now,int L,int R) {
    if(l>=L&&r<=R) {    
        return mi[now];   
    } 
    ll re=inf;   
    int mid=(l+r)>>1;   
    if(tree[now]) {
        re=min(re, calc(s[tree[now]], dis[idx[max(l,L)]]));
        re=min(re, calc(s[tree[now]], dis[idx[min(r,R)]]));        
    }
    if(L<=mid) re=min(re, query(l,mid,ls,L,R)); 
    if(R>mid)  re=min(re, query(mid+1,r,rs,L,R));   
    return re;    
}
// 将线段 x 插入到 [L,R] 当中.  
void update(int l,int r,int now,int L,int R,int x) {
    if(l>r) return ;  
    if(l>=L&&r<=R) { 
        if(!tree[now]) tree[now]=x; 
        else {    
            int mid=(l+r)>>1;   
            ll key=dis[idx[mid]];         
            // x is better than tree[now]  
            if(calc(s[tree[now]], key)>calc(s[x], key)) 
                swap(tree[now], x);  
            // x is bad segment 
            if(calc(s[x], pl[now]) < calc(s[tree[now]], pl[now]) && l!=r)
                update(l,mid,ls,L,R,x); 
            if(calc(s[x], pr[now]) < calc(s[tree[now]], pr[now]) && l!=r) 
                update(mid+1,r,rs,L,R,x);   
        }     
        mi[now]=get(now);       
        if(l!=r) mi[now]=min(mi[now], min(mi[ls], mi[rs]));  
        return ; 
    }
    int mid=(l+r)>>1; 
    if(L<=mid)  update(l,mid,ls,L,R,x); 
    if(R>mid)   update(mid+1,r,rs,L,R,x); 
    pushup(now);   
}     
void add(int u,int v,int c) {
    nex[++edges]=hd[u]; 
    hd[u]=edges; 
    to[edges]=v; 
    val[edges]=c; 
}
void dfs1(int x, int ff) {  
    fa[x]=ff,size[x]=1,dep[x]=dep[ff]+1;       
    for(int i=hd[x];i;i=nex[i]) {
        int v=to[i]; 
        if(v==ff) continue;  
        dis[v]=dis[x]+val[i];   
        dfs1(v, x); 
        size[x]+=size[v]; 
        if(size[v]>size[son[x]]) son[x]=v;  
    }
}
void dfs2(int x, int tp) {
    top[x]=tp;   
    idx[dfn[x]=++scc]=x;          
    if(son[x]) dfs2(son[x], tp); 
    for(int i=hd[x];i;i=nex[i]) {
        int v=to[i]; 
        if(v==fa[x]||v==son[x]) continue;  
        dfs2(v, v); 
    }
}
int get_lca(int x, int y) {
    while(top[x]!=top[y]) {
        if(dep[top[x]]>dep[top[y]]) swap(x,y); 
        y=fa[top[y]];    
    }
    return dep[x]<dep[y]?x:y;    
}
ll Query(int x, int y) {
    ll re=inf;  
    while(top[x]!=top[y]) {
        if(dep[top[x]]>dep[top[y]]) swap(x, y);  
        re=min(re, query(1, n, 1, dfn[top[y]], dfn[y]));  
        // printf("%lld\n",re);   
        y=fa[top[y]];   
    }
    if(dep[x]>dep[y]) swap(x, y);      
    return min(re, query(1, n, 1, dfn[x], dfn[y]));  
}
void ins(int x, int y) {
    while(top[x]!=top[y]) {        
        update(1, n, 1, dfn[top[x]], dfn[x], cn);  
        x=fa[top[x]];   
    }
    update(1, n, 1, dfn[y], dfn[x], cn); 
}
int main() {
    // setIO("input");
    scanf("%d%d",&n,&m); 
    for(int i=1;i<n;++i) {
        int x,y,z; 
        scanf("%d%d%d",&x,&y,&z);  
        add(x,y,z); 
        add(y,x,z);  
    }
    dfs1(1, 0); 
    dfs2(1, 1);    
    build(1, n, 1);         
    for(int i=1;i<=m;++i) {
        int op,ss,t,a0,b0;  
        scanf("%d",&op); 
        if(op==1) {
            scanf("%d%d%d%d",&ss,&t,&a0,&b0); 
            int lca=get_lca(ss, t);   
            ++cn;   
            s[cn].k=-a0;  
            s[cn].b=1ll*a0*dis[ss]+b0;       
            ins(ss, lca);       
            ++cn;  
            s[cn].k=a0;   
            s[cn].b=1ll*a0*(dis[ss]-2*dis[lca])+b0;    
            ins(t, lca);     
        }
        else {
            scanf("%d%d",&ss,&t);   
            printf("%lld\n",Query(ss, t));   
        }
    }
    return 0; 
}

  

 

posted @ 2021-09-06 17:06  guangheli  阅读(94)  评论(0)    收藏  举报