线段树【模板】全面

过题之前先把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[rt]=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+=getnum(L,R,l,mid,rt<<1);
    if(R>mid) z+=getnum(L,R,mid+1,r,rt<<1|1);
    pushup(rt);
    return z;
}
int main(){
    ios::sync_with_stdio(false);
    cin>>n>>m;
    for(int i=1;i<=n;i++){
        cin>>w[i];
    }
    build(1,n,1);
    string opp;
    int x,y;
    while(m--){
        cin>>opp>>x>>y;
       if(opp=="Ask"){
           res1=query(x,y,1,n,1);
           cout<<res1<<" ";
           cout<<getnum(x,y,1,n,1)<<endl;
       }else{
            add(x,y,1,n,1);
       }
    }
    return 0;
}

更新加和(非替换)及查询最大值

模板题注意这道题c有负数。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=100100;
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 pushdown(int rt){
    //ln rn 左 右 子树的节点个数
    if(lazy[rt]){
        lazy[rt<<1]+=lazy[rt];
        lazy[rt<<1|1]+=lazy[rt];
        ans[rt<<1]+=lazy[rt];
        ans[rt<<1|1]+=lazy[rt];
        lazy[rt]=0;
    }
}
void add(int L,int c,int l,int r,int rt){
    if(l==r){
        ans[rt]+=c;
        lazy[rt]+=c;
        return ;
    }
    pushdown(rt);
    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;
        lazy[rt]+=c;
        return ;
    }
    pushdown(rt);
    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];
    }
    pushdown(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(){
    ios::sync_with_stdio(false);
    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==2){
            cout<<query(l,r,1,n,1)<<endl;
        }else{
            ll c;
            cin>>c;
            update(l,r,c,1,n,1);
        }
    }
    return 0;
}

变型 2020 CCPC MianYang J

待补吧。可以用线段树做。最好明天热身赛前写一下。
祝好运。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值