线段树

\(NOIP\)阶段只要不涉及区间翻转、区间插入的序列问题都可以用线段树实现。

每一个节点维护一个区间的信息。

线段树有分治的感觉。

线段树可以处理很多符合结合律的操作。

时间复杂度,建树为\(O(n)\),区间查询和区间修改为\(O(log\ n)\)

空间要开原序列长度的四倍。

查询的话就是序列分割,由于分割次数近似\(log\) \(n\),故总复杂度为\(O(n\ log\ n)\)

\(code:\)

void pushup(int cur)
{
    val[cur]=val[ls[cur]]+val[rs[cur]];
}
void pushdown(int cur,int l,int r)
{
    if(!lazy[cur]) return;
    lazy[ls[cur]]+=lazy[cur];
    lazy[rs[cur]]+=lazy[cur];
    int mid=(l+r)>>1;
    val[ls[cur]]+=lazy[cur]*(mid-l+1);
    val[rs[cur]]+=lazy[cur]*(r-mid);
    lazy[cur]=0;
}
void build(int L,int R,int &cur)
{
    cur=++tree_cnt;
    if(L==R)
    {
        val[cur]=a[L];
        return;
    }
    int mid=(L+R)>>1;
    build(L,mid,ls[cur]);
    build(mid+1,R,rs[cur]);
    pushup(cur);
}
void modify(int L,int R,int l,int r,ll add,int cur)
{
    if(L<=l&&R>=r)
    {
        val[cur]+=add*(r-l+1);
        lazy[cur]+=add;
        return;
    }
    pushdown(cur,l,r);
    int mid=(l+r)>>1;
    if(L<=mid) modify(L,R,l,mid,add,ls[cur]);
    if(R>mid) modify(L,R,mid+1,r,add,rs[cur]);
    pushup(cur);
}
ll query(int L,int R,int l,int r,int cur)
{
    if(L<=l&&R>=r) return val[cur];
    pushdown(cur,l,r);
    ll sum=0;
    int mid=(l+r)>>1;
    if(L<=mid) sum+=query(L,R,l,mid,ls[cur]);
    if(R>mid) sum+=query(L,R,mid+1,r,rs[cur]);
    return sum;
}

......

build(1,n,root);//建树
modify(x,y,1,n,z,root);//区间修改,x到y的区间加上z
query(x,y,1,n,root)//区间查询,x到y的和

动态开点

\(code:\)

void pushup(int cur)
{
    val[cur]=val[ls[cur]]+val[rs[cur]];
}
void pushdown(int cur,int l,int r)
{
    if(!lazy[cur]) return;
    int mid=(l+r)>>1;
    if(!ls[cur]) ls[cur]=++tree_cnt;
    lazy[ls[cur]]+=lazy[cur];
    val[ls[cur]]+=lazy[cur]*(mid-l+1);
    if(!rs[cur]) rs[cur]=++tree_cnt;
	lazy[rs[cur]]+=lazy[cur];
    val[rs[cur]]+=lazy[cur]*(r-mid);
    lazy[cur]=0;
}
void modify(int L,int R,int l,int r,ll add,int &cur)
{
    if(!cur) cur=++tree_cnt;
	if(L<=l&&R>=r)
    {
        val[cur]+=add*(r-l+1);
        lazy[cur]+=add;
        return;
    }
    pushdown(cur,l,r);
    int mid=(l+r)>>1;
    if(L<=mid) modify(L,R,l,mid,add,ls[cur]);
    if(R>mid) modify(L,R,mid+1,r,add,rs[cur]);
    pushup(cur);
}
ll query(int L,int R,int l,int r,int cur)
{
    if(L<=l&&R>=r) return val[cur];
    pushdown(cur,l,r);
    ll sum=0;
    int mid=(l+r)>>1;
    if(L<=mid) sum+=query(L,R,l,mid,ls[cur]);
    if(R>mid) sum+=query(L,R,mid+1,r,rs[cur]);
    return sum;
}

线段树合并

\(x\)\(y\)合并为\(x\)

通常用来合并权值线段树(一个节点权值为其值域区间元素个数)

更新信息时要特判叶子节点的情况,防止信息覆盖,代码为雨天的尾巴

\(code:\)

int merge(int x,int y,int l,int r)
{
    if(!x||!y) return x+y;
    int p=++tree_cnt;
    if(l==r) ma[p]=ma[x]+ma[y],id[p]=l;
    else
    {
        ls[p]=merge(ls[x],ls[y],l,mid);
        rs[p]=merge(rs[x],rs[y],mid+1,r);
        pushup(p);
    }
    return p;
}

线段树分裂

\(code:\)

void pushup(int cur)
{
    cnt[cur]=cnt[ls[cur]]+cnt[rs[cur]];
}
void modify(int l,int r,int pos,int v,int &cur)
{
    if(!cur) cur=++tot;
    if(l==r)
    {
        cnt[cur]+=v;
        return;
    }
    if(pos<=mid) modify(l,mid,pos,v,ls[cur]);
    else modify(mid+1,r,pos,v,rs[cur]);
    pushup(cur);
}
int query(int L,int R,int l,int r,int cur)
{
    if(!cur) return 0;
    if(L<=l&&R>=r) return cnt[cur];
    int v=0;
    if(L<=mid) v+=query(L,R,l,mid,ls[cur]);
    if(R>mid) v+=query(L,R,mid+1,r,rs[cur]);
    return v;
}
int kth(int l,int r,int k,int cur)
{
    if(l==r) return l;
    if(cnt[ls[cur]]>=k) return kth(l,mid,k,ls[cur]);
    else return kth(mid+1,r,k-cnt[ls[cur]],rs[cur]);
}
int merge(int x,int y)
{
    if(!x||!y) return x+y;
    int p=++tot;
    cnt[p]=cnt[x]+cnt[y];
    ls[p]=merge(ls[x],ls[y]);
    rs[p]=merge(rs[x],rs[y]);
    return p;
}
void split(int L,int R,int l,int r,int &x,int &y)
{
    if(!x) return;
    if(L<=l&&R>=r)
    {
        y=x,x=0;
        return;
    }
    y=++tot;
    if(L<=mid) split(L,R,l,mid,ls[x],ls[y]);
    if(R>mid) split(L,R,mid+1,r,rs[x],rs[y]);
    pushup(x),pushup(y);
}

线段树优化建图

\(code:\)

void build_in(int L,int R,int &cur)
{
    cur=++tree_cnt;
    if(L==R)
    {
        in_num[L]=cur;
        return;
    }
    int mid=(L+R)>>1;
    build_in(L,mid,ls[cur]);
    build_in(mid+1,R,rs[cur]);
    add(ls[cur],cur,0),add(rs[cur],cur,0);
}
void build_out(int L,int R,int &cur)
{
    cur=++tree_cnt;
    if(L==R)
    {
        out_num[L]=cur;
        return;
    }
    int mid=(L+R)>>1;
    build_out(L,mid,ls[cur]);
    build_out(mid+1,R,rs[cur]);
    add(cur,ls[cur],0),add(cur,rs[cur],0);
}
void modify_in(int L,int R,int l,int r,int pos,int val,int &cur)
{
    if(L<=l&&R>=r)
    {
        add(cur,pos,val);
        return;
    }
    int mid=(l+r)>>1;
    if(L<=mid) modify_in(L,R,l,mid,pos,val,ls[cur]);
    if(R>mid) modify_in(L,R,mid+1,r,pos,val,rs[cur]);
}
void modify_out(int L,int R,int l,int r,int pos,int val,int &cur)
{
    if(L<=l&&R>=r)
    {
        add(pos,cur,val);
        return;
    }
    int mid=(l+r)>>1;
    if(L<=mid) modify_out(L,R,l,mid,pos,val,ls[cur]);
    if(R>mid) modify_out(L,R,mid+1,r,pos,val,rs[cur]);
}

线段树分治

维护时间区间,对询问的时间轴进行分治,每个修改操作会有一个作用的区间,将修改标记到线段树上,\(dfs\)一遍线段树来统计答案

连通图:删去无向连通图一些边后,询问原图是否连通

将删边转化为边存在的区间,用带权并查集维护连通块大小

\(code:\)

void build(int l,int r,int &cur)
{
    if(!cur) cur=++tree_cnt;
    if(l==r) return;
    int mid=(l+r)>>1;
    build(l,mid,ls[cur]);
    build(mid+1,r,rs[cur]);
}
void insert(int L,int R,int l,int r,int id,int cur)
{
    if(L<=l&&R>=r)
    {
        v[cur].push_back(id);
        return;
    }
    int mid=(l+r)>>1;
    if(L<=mid) insert(L,R,l,mid,id,ls[cur]);
    if(R>mid) insert(L,R,mid+1,r,id,rs[cur]);
}
int find(int x)
{
    return fa[x]==x?x:find(fa[x]);
}
void merge(int x,int y)
{
    int rx=find(x),ry=find(y);
    if(rx==ry) return;
    if(de[rx]<de[ry]) swap(rx,ry);
    st[++top]=(node){rx,ry,de[rx]};
    fa[ry]=rx,siz[rx]+=siz[ry];
    de[rx]=max(de[rx],de[ry]+1);
}
void del(int id)
{
    int x=st[id].x,y=st[id].y;
    fa[y]=y,siz[x]-=siz[y],de[x]=st[id].deep;
}
void dfs(int l,int r,int cur)
{
    int now=top,size=v[cur].size();
    for(int i=0;i<size;++i) 
        merge(e[v[cur][i]].x,e[v[cur][i]].y);
    if(l==r) ans[l]=(siz[find(1)]==n);
    else
    {
        int mid=(l+r)>>1;
        dfs(l,mid,ls[cur]),dfs(mid+1,r,rs[cur]);
    }
    while(top>now) del(top--);
}

Shortest Path Queries:维护一个无向连通图,边有边权,支持加边删边和询问从\(x\)\(y\)的异或最短路

\(code:\)

void build(int l,int r,int &cur)
{
    if(!cur) cur=++tree_cnt;
    if(l==r) return;
    int mid=(l+r)>>1;
    build(l,mid,ls[cur]);
    build(mid+1,r,rs[cur]);
}
void insert(int L,int R,int l,int r,int id,int cur)
{
    if(L<=l&&R>=r)
    {
        v[cur].push_back(id);
        return;
    }
    int mid=(l+r)>>1;
    if(L<=mid) insert(L,R,l,mid,id,ls[cur]);
    if(R>mid) insert(L,R,mid+1,r,id,rs[cur]);
}
void ins(int x,int cur)
{
    for(int i=30;i>=0;--i)
    {
        if(x&(1<<i))
        {
            if(!a[cur][i])
            {
                a[cur][i]=x;
                break;
            }
            else x^=a[cur][i];
        }
    }
}
int get(int x,int cur)
{
    for(int i=30;i>=0;--i)
        if((x^a[cur][i])<x)
            x^=a[cur][i];
    return x;
}
int find(int x)
{
    return fa[x]==x?x:find(fa[x]);
}
int xor_dis(int x)
{
    return fa[x]==x?dis[x]:dis[x]^xor_dis(fa[x]);
}
void merge(int x,int y,int v)
{
    if(de[x]<de[y]) swap(x,y);
    st[++top]=(node){x,y,de[x]};
    fa[y]=x,dis[y]=v,de[x]=max(de[x],de[y]+1);
}
void del(int id)
{
    int x=st[id].x,y=st[id].y;
    fa[y]=y,dis[y]=0,de[x]=st[id].deep;
}
void copy(int x,int y)
{
    for(int i=0;i<=30;++i) a[y][i]=a[x][i];
}
void dfs(int l,int r,int cur)
{
    int now=top,size=v[cur].size();
    for(int i=0;i<size;++i)
    {
        int id=v[cur][i],x=e[id].x,y=e[id].y,v=e[id].v;
        v^=xor_dis(x)^xor_dis(y),x=find(x),y=find(y);
        if(x==y) ins(v,cur);
        else merge(x,y,v);
    }
    if(l==r)
    {
        int x=qu[l].x,y=qu[l].y;
        ans[l]=get(xor_dis(x)^xor_dis(y),cur);
    }
    else
    {
        int mid=(l+r)>>1;
        copy(cur,ls[cur]),dfs(l,mid,ls[cur]);
        copy(cur,rs[cur]),dfs(mid+1,r,rs[cur]);
    }
    while(top>now) del(top--);
}

李超线段树

用来维护坐标系上的线段,支持添加线段和询问

每个线段树上每个区间维护一个优势线段,其至少覆盖了该区间的一半,维护时分类讨论即可

Blue Mary开公司

\(code:\)

double y(int id,int x)
{
    return k[id]*(x-1)+b[id];
}
void modify(int l,int r,int id,int &cur)
{
    if(!cur) cur=++tree_cnt;
    if(y(id,l)>y(val[cur],l)&&y(id,r)>y(val[cur],r))
    {
        val[cur]=id;
        return;
    }
    if(y(id,l)<=y(val[cur],l)&&y(id,r)<=y(val[cur],r)) return;
    int mid=(l+r)>>1;
    if(k[id]>k[val[cur]])
    {
        if(y(id,mid)>y(val[cur],mid)) 
            modify(l,mid,val[cur],ls[cur]),val[cur]=id;
        else modify(mid+1,r,id,rs[cur]);
    }
    else
    {
        if(y(id,mid)>y(val[cur],mid)) 
            modify(mid+1,r,val[cur],rs[cur]),val[cur]=id;
        else modify(l,mid,id,ls[cur]);
    }
}
double query(int l,int r,int x,int cur)
{
    if(l==r) return y(val[cur],x);
    int mid=(l+r)>>1;
    double ans=y(val[cur],x);
    if(x<=mid) return max(ans,query(l,mid,x,ls[cur]));
    else return max(ans,query(mid+1,r,x,rs[cur]));
}

Segment

\(code:\)

double y(int id,int x)
{
    return k[id]*x+b[id];
}
bool check(int a,int b,int x)
{
    return fabs(y(a,x)-y(b,x))>eps?y(a,x)<y(b,x):a>b;
}
void modify(int L,int R,int l,int r,int id,int &cur)
{
    if(!cur) cur=++tree_cnt;
    int mid=(l+r)>>1;
    if(L<=l&&R>=r)
    {
        if(check(id,val[cur],l)&&check(id,val[cur],r)) return;
        if(!check(id,val[cur],l)&&!check(id,val[cur],r))
        {
            val[cur]=id;
            return;
        }
        if(!check(id,val[cur],mid)) swap(id,val[cur]);
        if(!check(id,val[cur],l)) modify(L,R,l,mid,id,ls[cur]);
        else modify(L,R,mid+1,r,id,rs[cur]);
        return;
    }
    if(L<=mid) modify(L,R,l,mid,id,ls[cur]);
    if(R>mid) modify(L,R,mid+1,r,id,rs[cur]);
}
int query(int l,int r,int x,int cur)
{
    if(l==r) return val[cur];
    int mid=(l+r)>>1,ans;
    if(x<=mid) ans=query(l,mid,x,ls[cur]);
    else ans=query(mid+1,r,x,rs[cur]);
    if(check(ans,val[cur],x)) ans=val[cur];
    return ans;
}

吉司机线段树

【模板】线段树 3

\(code:\)

void pushup(int cur)
{
    sum[cur]=sum[ls]+sum[rs],ma[cur]=max(ma[ls],ma[rs]),hma[cur]=max(hma[ls],hma[rs]);
    if(ma[ls]==ma[rs]) se[cur]=max(se[ls],se[rs]),cnt[cur]=cnt[ls]+cnt[rs];
    else if(ma[ls]>ma[rs]) se[cur]=max(se[ls],ma[rs]),cnt[cur]=cnt[ls];
    else se[cur]=max(ma[ls],se[rs]),cnt[cur]=cnt[rs];
}
void pushtag(int cur,int l,int r,ll v,ll hv,ll w,ll hw)
{
    sum[cur]+=v*cnt[cur]+w*(r-l+1-cnt[cur]);
    hma[cur]=max(hma[cur],ma[cur]+hv),ma[cur]+=v;
    htag[cur]=max(htag[cur],tag[cur]+hv),tag[cur]+=v;
    hadd[cur]=max(hadd[cur],add[cur]+hw),add[cur]+=w;
    if(se[cur]!=-inf) se[cur]+=w;
}
void pushdown(int cur,int l,int r)
{
    ll maxv=max(ma[ls],ma[rs]);
    if(ma[ls]==maxv) pushtag(ls,l,mid,tag[cur],htag[cur],add[cur],hadd[cur]);
    else pushtag(ls,l,mid,add[cur],hadd[cur],add[cur],hadd[cur]);
    if(ma[rs]==maxv) pushtag(rs,mid+1,r,tag[cur],htag[cur],add[cur],hadd[cur]);
    else pushtag(rs,mid+1,r,add[cur],hadd[cur],add[cur],hadd[cur]);
    tag[cur]=htag[cur]=add[cur]=hadd[cur]=0;
}
void build(int l,int r,int cur)
{
    if(l==r)
    {
        sum[cur]=ma[cur]=hma[cur]=a[l],se[cur]=-inf,cnt[cur]=1;
        return;
    }
    build(l,mid,ls),build(mid+1,r,rs),pushup(cur);
}
void modify_add(int L,int R,int l,int r,ll v,int cur)
{
    if(L<=l&&R>=r)
    {
        pushtag(cur,l,r,v,v,v,v);
        return;
    }
    pushdown(cur,l,r);
    if(L<=mid) modify_add(L,R,l,mid,v,ls);
    if(R>mid) modify_add(L,R,mid+1,r,v,rs);
    pushup(cur);
}
void modify_min(int L,int R,int l,int r,ll v,int cur)
{
    if(v>=ma[cur]) return;
    if(L<=l&&R>=r&&v>se[cur])
    {
        pushtag(cur,l,r,v-ma[cur],v-ma[cur],0,0);
        return;
    }
    pushdown(cur,l,r);
    if(L<=mid) modify_min(L,R,l,mid,v,ls);
    if(R>mid) modify_min(L,R,mid+1,r,v,rs);
    pushup(cur);
}
ll query_sum(int L,int R,int l,int r,int cur)
{
    if(L<=l&&R>=r) return sum[cur];
    pushdown(cur,l,r);
    ll ans=0;
    if(L<=mid) ans+=query_sum(L,R,l,mid,ls);
    if(R>mid) ans+=query_sum(L,R,mid+1,r,rs);
    return ans;
}
ll query_max(int L,int R,int l,int r,int cur)
{
    if(L<=l&&R>=r) return ma[cur];
    pushdown(cur,l,r);
    ll ans=-inf;
    if(L<=mid) ans=max(ans,query_max(L,R,l,mid,ls));
    if(R>mid) ans=max(ans,query_max(L,R,mid+1,r,rs));
    return ans;
}
ll query_his(int L,int R,int l,int r,int cur)
{
    if(L<=l&&R>=r) return hma[cur];
    pushdown(cur,l,r);
    ll ans=-inf;
    if(L<=mid) ans=max(ans,query_his(L,R,l,mid,ls));
    if(R>mid) ans=max(ans,query_his(L,R,mid+1,r,rs));
    return ans;
}

CPU监控

\(code:\)

void pushup(int cur)
{
    ma[cur]=max(ma[ls],ma[rs]),hma[cur]=max(hma[ls],hma[rs]);
}
void pushadd(int cur,ll v,ll hv)
{
    hma[cur]=max(hma[cur],ma[cur]+hv),ma[cur]+=v;
    if(!tag[cur]) hadd[cur]=max(hadd[cur],add[cur]+hv),add[cur]+=v;
    else hcov[cur]=max(hcov[cur],cov[cur]+hv),cov[cur]+=v;
}
void pushcov(int cur,ll v,ll hv)
{
    tag[cur]=1;
    hma[cur]=max(hma[cur],hv),ma[cur]=v;
    hcov[cur]=max(hcov[cur],hv),cov[cur]=v;
}
void pushdown(int cur)
{
    if(add[cur]) pushadd(ls,add[cur],hadd[cur]),pushadd(rs,add[cur],hadd[cur]);
    if(tag[cur]) pushcov(ls,cov[cur],hcov[cur]),pushcov(rs,cov[cur],hcov[cur]);
    add[cur]=hadd[cur]=tag[cur]=cov[cur]=hcov[cur]=0;
}
void build(int l,int r,int cur)
{
    if(l==r)
    {
        ma[cur]=hma[cur]=a[l];
        return;
    }
    build(l,mid,ls),build(mid+1,r,rs),pushup(cur);
}
void modify_add(int L,int R,int l,int r,ll v,int cur)
{
    if(L<=l&&R>=r)
    {
        pushadd(cur,v,v);
        return;
    }
    pushdown(cur);
    if(L<=mid) modify_add(L,R,l,mid,v,ls);
    if(R>mid) modify_add(L,R,mid+1,r,v,rs);
    pushup(cur);
}
void modify_cov(int L,int R,int l,int r,ll v,int cur)
{
    if(L<=l&&R>=r)
    {
        pushcov(cur,v,v);
        return;
    }
    pushdown(cur);
    if(L<=mid) modify_cov(L,R,l,mid,v,ls);
    if(R>mid) modify_cov(L,R,mid+1,r,v,rs);
    pushup(cur);
}
ll query_max(int L,int R,int l,int r,int cur)
{
    if(L<=l&&R>=r) return ma[cur];
    pushdown(cur);
    ll ans=-inf;
    if(L<=mid) ans=max(ans,query_max(L,R,l,mid,ls));
    if(R>mid) ans=max(ans,query_max(L,R,mid+1,r,rs));
    return ans;
}
ll query_his(int L,int R,int l,int r,int cur)
{
    if(L<=l&&R>=r) return hma[cur];
    pushdown(cur);
    ll ans=-inf;
    if(L<=mid) ans=max(ans,query_his(L,R,l,mid,ls));
    if(R>mid) ans=max(ans,query_his(L,R,mid+1,r,rs));
    return ans;
}
posted @ 2020-01-22 19:53  lhm_liu  阅读(260)  评论(1编辑  收藏  举报