线段树入门

线段树的基本操作:

1.单点修改+区间查

复制代码
#include<bits/stdc++.h>
#define lson L,(L+R)/2,p*2
#define rson (L+R)/2+1,R,p*2+1
#define E(i,n) for(int i=1;i<=n;++i)
#define F(i,l,r) for(int i=l;i<=r;++i)
#define mid (tr[p].l+tr[p].r)/2
#define ll long long
using namespace std;
const int N=1e5+5;
struct node{
    int l,r; ll w;
}tr[N<<2];//1.开四倍空间 
int n,m,a[N];
inline void pushup(int p){ tr[p].w=tr[p*2].w+tr[p*2+1].w; }
inline void build(int L,int R,int p){
    tr[p]=(node){L,R,a[L]};
    if(L==R) return;
    build(lson); build(rson);
    pushup(p);//2.记得pushup
}
inline void upd(int x,int y,int p){
    if(tr[p].l==tr[p].r) { tr[p].w+=y; return ; }
    if(x<=mid) upd(x,y,p*2);
    else upd(x,y,p*2+1);
    pushup(p);
}
inline ll query(int L,int R,int p){
    if(tr[p].l>=L && tr[p].r<=R) return tr[p].w;
    ll res=0;
    if(L<=mid) res+=query(L,R,p*2);
    if(R>mid) res+=query(L,R,p*2+1);
    return res;
    //3.两个if建议画图分析。等号给一边即可,不然会算重
}
int main(){
    scanf("%d%d",&n,&m); for(int i=1;i<=n;++i) scanf("%d",&a[i]);
    build(1,n,1);
    char op[10]; int x,y;
    while(m--){
        scanf("%s%d%d",op,&x,&y);
        if(op[0]=='A') upd(x,y,1);
        if(op[0]=='S') upd(x,-y,1);
        if(op[0]=='Q') printf("%lld\n",query(x,y,1));
    }
    return 0;
}
复制代码

 2.区间修+区间查

复制代码
inline void pushup(int p){ tr[p].w=tr[ls].w+tr[rs].w; }
inline void pushdown(int p){//更新tag 
    if(tr[p].tag){
        tr[ls].tag+=tr[p].tag; tr[rs].tag+=tr[p].tag;
        tr[ls].w+=(tr[ls].r-tr[ls].l+1) * tr[p].tag;
        tr[rs].w+=(tr[rs].r-tr[rs].l+1) * tr[p].tag;
        tr[p].tag=0;
    }
}
inline void upd(int x,int y,int z,int p){
    if(tr[p].l>=x && tr[p].r<=y) { tr[p].w+=(tr[p].r-tr[p].l+1)*z; tr[p].tag+=z; return ; }
    //w 和 tag都要更新 
    pushdown(p);
    if(x<=mid) upd(x,y,z,ls);
    if(y>mid) upd(x,y,z,rs);
    //不能写if-else ,因为不是"非左即右" 
    pushup(p);
}
inline ll query(int x,int y,int p){
    if(tr[p].l>=x && tr[p].r<=y) return tr[p].w;
    pushdown(p); //对比单点修,只多了一步pushdown
   ll res
=0; if(mid>=x) res+=query(x,y,ls); if(mid<y) res+=query(x,y,rs); // pushup(p); return res; }
复制代码

 3.单点加+区间最值

复制代码
//变化:pushup 与 qry 
inline void pushup(int p){ tr[p].w=max(tr[ls].w,tr[rs].w); }
inline int qry(int x,int y,int p){
    if(x<=tr[p].l && y>=tr[p].r) return tr[p].w;
    int res=0;
    if(x<=mid) res=max(res,qry(x,y,ls));
    if(y>mid) res=max(res,qry(x,y,rs));
    return res;
}
复制代码

4.区间修+单点查

//tr[].w只存变化量
//不用pushup,因为是单点查,所以没有必要维护区间状态,在涉及到的这条链上只需要有这个tag就行 
//pushdown的变化也是这个原因:不关心区间状态,只要询问的单点所在的链上有这个tag,用到的时候下传就可以 inline void pushdown(int p){ if(tr[p].w) tr[ls].w+=tr[p].w , tr[rs].w+=tr[p].w , tr[p].w=0; }

 

posted @   superl61  阅读(10)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示