线段树模板全面

Posted iuk11

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了线段树模板全面相关的知识,希望对你有一定的参考价值。

过题之前先把cin换成scanf

点/区间线段树求和

线段树,顾名思义,是把给定的n个元素组成的序列比作线段,以树的方式重建,进而达到更新,询问的时间复杂度都达到 log ⁡ 2 n \\log_2n log2n的级别。
pushup建树过程中或者更新过程中,会先递归到叶子节点(不一定到叶子节点),然后再回溯,就是下一次回到他们两个的子树根节点,该点要记录两个子节点的和,所以pushup.当然再向上回溯同理,也是当前根等于他两个子节点的权值和。
pushdown当我们更新区间的时候,不需要把该区间需要加的数传递下去更新,只需要用lazy[]数组存放在当前区间上面即可。如果我需要访问这个区间内的子区间,就需要把lazy[]中的值传递下去(如果有值),因为递归回来会更新权值和,两个子节点相加的和会覆盖掉当前节点的值,如果不下传,会产生子树和错误。

void pushup(int rt){
    ans[rt]=ans[rt<<1]+ans[rt<<1|1];
}
void pushdown(int rt,int ln,int rn){
    //ln rn 左 右 子树的节点个数
    if(lazy[rt]){
        lazy[rt<<1]+=lazy[rt];
        lazy[rt<<1|1]+=lazy[rt];
        ans[rt<<1]+=lazy[rt]*ln;
        ans[rt<<1|1]+=lazy[rt]*rn;
        lazy[rt]=0;
    }
}

build首先先递归到叶子节点,然后从叶子结点开始向上回溯。每个叶子节点都对应着其中的一个输入值,我们不断二分回溯将子树和记录在当前的子树根上,最后完成整棵树的建立。

void build(int l,int r,int rt){
    if(l==r){
        ans[l]=a[l];
        return ;
    }
    int mid=(l+r)>>1;
    build(1,mid,rt<<1);
    build(mid+1,r,rt<<1|1);
    pushup(rt);
}

整体代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=50010;
ll a[N],ans[N<<2],lazy[N<<2];
int n,q;
int l,r;
void pushup(int rt){
    ans[rt]=ans[rt<<1]+ans[rt<<1|1];
}
void build(int l,int r,int rt){
    if(l==r){
        ans[l]=a[l];
        return ;
    }
    int mid=(l+r)>>1;
    build(1,mid,rt<<1);
    build(mid+1,r,rt<<1|1);
    pushup(rt);
}
void pushdown(int rt,int ln,int rn){
    //ln rn 左 右 子树的节点个数
    if(lazy[rt]){
        lazy[rt<<1]+=lazy[rt];
        lazy[rt<<1|1]+=lazy[rt];
        ans[rt<<1]+=lazy[rt]*ln;
        ans[rt<<1|1]+=lazy[rt]*rn;
        lazy[rt]=0;
    }
}
void add(int L,int c,int l,int r,int rt){
    //L点 +c
    if(l==r){
        ans[rt]+=c;
        return ;
    }
    int mid=(l+r)>>1;
    //pushdown(rt,mid-l+1,r-mid);
    if(L<=mid) add(L,c,l,mid,rt<<1);
    else add(L,c,mid+1,r,rt<<1|1);
    pushup(rt);
}
void update(int L,int R,int c,int l,int r,int rt){
    if(L<=l&&r<=R){
        ans[rt]+=c*(r-l+1);
        lazy[rt]+=c;
        return ;
    }
    int mid=(l+r)>>1;
    pushdown(rt,mid-l+1,r-mid);
    if(L<=mid) update(L,R,c,l,mid,rt<<1);
    if(R>mid) update(L,R,c,mid+1,r,rt<<1|1);
    pushup(rt);
}
ll query(int L,int R,int l,int r,int rt){
    if(L<=l&&r<=R){
        return ans[rt];
    }
    int mid=(l+r)>>1;
    pushdown(rt,mid-l+1,r-mid);
    ll ans=0;
    if(L<=mid) ans+=query(L,R,l,mid,rt<<1);
    if(R>mid) ans+=query(L,R,mid+1,r,rt<<1|1);
    return ans;
}
int main(){
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>a[i];
    }
    build(1,n,1);
    cin>>q;
    while(q--){
        int c;
        cin>>l>>r;
        cin>>c;
        //add(l,c,1,n,1); 点更新
        //update(l,r,c,1,n,1); 区间更新
        //query(l,r,1,n,1); 区间查询
    }
    return 0;
}

点/区间线段树求最大值

hdu 1754 模板题

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=50010;
ll a[N],ans[N<<2],lazy[N<<2];
int n,q;
int l,r;
void pushup(int rt){
    ans[rt]=max(ans[rt<<1],ans[rt<<1|1]);
}
void build(int l,int r,int rt){//建树
    if(l==r){
        ans[rt]=a[l];
        return ;
    }
    int mid=(l+r)>>1;
    build(l,mid,rt<<1);
    build(mid+1,r,rt<<1|1);
    pushup(rt);
}
void add(int L,int c,int l,int r,int rt){
    //L点 +c
    if(l==r){
        ans[rt]=c;
        return ;
    }
    int mid=(l+r)>>1;
    if(L<=mid) add(L,c,l,mid,rt<<1);
    else add(L,c,mid+1,r,rt<<1|1);
    pushup(rt);
}
void update(int L,int R,int c,int l,int r,int rt){
    if(L<=l&&r<=R){
        ans[rt]=c;
        return ;
    }
    int mid=(l+r)>>1;
    if(L<=mid) update(L,R,c,l,mid,rt<<1);
    if(R>mid) update(L,R,c,mid+1,r,rt<<1|1);
    pushup(rt);
}
ll query(int L,int R,int l,int r,int rt){
    if(L<=l&&r<=R){
        return ans[rt];
    }
    int mid=(l+r)>>1;
    ll res=0;
    if(L<=mid) res=max(res,query(L,R,l,mid,rt<<1));
    if(R>mid) res=max(res,query(L,R,mid+1,r,rt<<1|1));
    return res;
}
int main(){
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>a[i];
    }
    build(1,n,1);
    cin>>q;
    while(q--){
        int opt;
        cin>>opt>>l>>r;
        //cin>>c;
        //add(l,c,1,n,1); 点更新
        //update(l,r,c,1,n,1); 区间更新
        //query(l,r,1,n,1); 区间查询
        if(opt==1){
            cout<<query(l,r,1,n,1)<<endl;
        }else{
            int c;
            cin>>c;
            update(l,r,c,1,n,1);
        }
    }
    return 0;
}

查询最大值以及最大值个数

模板题

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+100;
typedef long long ll;
int n,m;
ll w[N],ans[N<<2],lazy[N<<2];
int cnt[N<<2];
void pushup(int rt){
    ans[rt]=max(ans[rt<<1],ans[rt<<1|1]);
    if(ans[rt<<1]==ans[rt<<1|1]){
        cnt[rt]=cnt[rt<<1]+cnt[rt<<1|1];
    }else if(ans[rt<<1] > ans[rt<<1|1]){
        cnt[rt]=cnt[rt<<1];
    }else{
        cnt[rt]=cnt[rt<<1|1];
    }
}
void build(int l,int r,int rt){
    if(l==r){
        ans[rt]=w[l];
        cnt[rt]=1;
        return ;
    }
    int mid=(l+r)>>1;
    build(l,mid,rt<<1);
    build(mid+1,r,rt<<1|1);
    pushup(rt);
}
void add(int L,int c,int l,int r,int rt){
    if(l==r&&l==L){
        ans[rt]=c;
        cnt[rt]=1;
        return ;
    }
    int mid=(l+r)>>1;
    if(L<=mid) add(L,c,l,mid,rt<<1);
    else add(L,c,mid+1,r,rt<<1|1);
    pushup(rt);
}
ll query(int L,int R,int l,int r,int rt){
    if(L<=l&&r<=R){
        return ans[rt];
    }
    int mid=(l+r)>>1;
    ll res=0;
    if(L<=mid) res=max(res,query(L,R,l,mid,rt<<1));
    if(R>mid) res=max(res,query(L,R,mid+1,r,rt<<1|1));
    return res;
}
ll res1=0;
int getnum(int L,int R,int l,int r,int rt){
    if(L<=l&&r<=R){
        if(res1<=ans[rt]){
            return cnt[rt];
        }else{
            return 0;
        }
    }
    int z=0;
    int mid=(l+r)>>1;
    if(L<=mid) z+=以上是关于线段树模板全面的主要内容,如果未能解决你的问题,请参考以下文章

Luogu P3372 模板线段树 1

超全面的线段树:从入门到入坟

洛谷P3372线段树模板1——线段树

线段树模板 (poj 3468)

线段树模板整理

线段树模板总结