其实已经写好一大半了,只是因为懒没有全写完一直在拖

推导

存储

首先我们要存储整个树

我们知道线段树是一颗完全二叉树

对一一个节点 $i$ ,它的左儿子是 $2i$ ,其右儿子是 $2i+1$,这很好证明,因为完全二叉树的性质

那么对一个数组 a ,我们把它的左端点和右端点 $[l,r]$ 设为根节点,并设根节点的编号为1,将区间一分为二,设mid=(l+r)>>1 ,数学表达式为$\lfloor l+r \rfloor $ ,(l+r的向下取整)

我们想到可以用递归解决这个问题,定义build(id,l,r) 表示区间 $[l,r]$ 的编号为 id ,可以写出代码

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

操作

像视频中的一样,我们用线段树维护一个最大值,每次操作选择区间 $[l,r]$ 将区间每个数+ $x$ ,

如果你听不明白

这里我做了一个网页实现了可视化,链接线段树-1755576145367.html

线段树可视化

当然觉得我可视化做的不行的也可以看另一个网站的,个人感觉也不错

https://visualgo.net/zh/segmenttree

模板

视频讲的是维护最大值,这里的模板是求和的

模板(区间求和)

#include<bits/stdc++.h>
using namespace std;
using ll=long long;

vector<ll>tree,lazy;

// 初始化线段树数组
void init(int n){tree.assign(4*n+5,0);lazy.assign(4*n+5,0);}

// 上推操作:维护父节点的值
void pull(int id){tree[id]=tree[id<<1]+tree[id<<1|1];}

// 将区间 [l,r] 全部加上 v(懒标记+更新节点值)
void apply(int id,int l,int r,ll v){tree[id]+=v*(r-l+1);lazy[id]+=v;}

// 下推懒标记,把当前节点的标记分发给子节点
void push(int id,int l,int r){
    if(lazy[id]==0)return;
    int m=(l+r)>>1;
    apply(id<<1,l,m,lazy[id]);
    apply(id<<1|1,m+1,r,lazy[id]);
    lazy[id]=0;
}

// 建树:用数组 a 初始化线段树
void build(int id,int l,int r,const vector<ll>&a){
    if(l==r){tree[id]=a[l];return;}
    int m=(l+r)>>1;
    build(id<<1,l,m,a);
    build(id<<1|1,m+1,r,a);
    pull(id);
}

// 区间加法:把 [L,R] 内每个元素都加 v
void add(int id,int l,int r,int L,int R,ll v){
    if(L<=l&&r<=R){apply(id,l,r,v);return;}
    push(id,l,r);
    int m=(l+r)>>1;
    if(L<=m)add(id<<1,l,m,L,R,v);
    if(R>m)add(id<<1|1,m+1,r,L,R,v);
    pull(id);
}

// 区间求和:查询 [L,R] 的元素和
ll sumq(int id,int l,int r,int L,int R){
    if(L<=l&&r<=R)return tree[id];
    push(id,l,r);
    int m=(l+r)>>1;ll s=0;
    if(L<=m)s+=sumq(id<<1,l,m,L,R);
    if(R>m)s+=sumq(id<<1|1,m+1,r,L,R);
    return s;
}

int main(){
    ios::sync_with_stdio(0);cin.tie(0);
    int n;cin>>n;
    vector<ll>a(n+1);
    for(int i=1;i<=n;i++)cin>>a[i];
    init(n);
    build(1,1,n,a);
    int q;cin>>q;
    while(q--){
        int op;cin>>op;
        if(op==1){
            int L,R;ll v;cin>>L>>R>>v;
            add(1,1,n,L,R,v);       // 区间加
        }else{
            int L,R;cin>>L>>R;
            cout<<sumq(1,1,n,L,R)<<"\n"; // 区间和
        }
    }
}

区间最大值+区间加

#include<bits/stdc++.h>
using namespace std;
using ll=long long;

vector<ll>tree,lazy;

void init(int n){tree.assign(4*n+5,0);lazy.assign(4*n+5,0);}

void pull(int id){tree[id]=max(tree[id<<1],tree[id<<1|1]);}

void apply(int id,ll v){tree[id]+=v;lazy[id]+=v;}

void push(int id,int l,int r){
    if(lazy[id]==0)return;
    int m=(l+r)>>1;
    apply(id<<1,lazy[id]);
    apply(id<<1|1,lazy[id]);
    lazy[id]=0;
}

void build(int id,int l,int r,const vector<ll>&a){
    if(l==r){tree[id]=a[l];return;}
    int m=(l+r)>>1;
    build(id<<1,l,m,a);
    build(id<<1|1,m+1,r,a);
    pull(id);
}

void add(int id,int l,int r,int L,int R,ll v){
    if(L<=l&&r<=R){apply(id,v);return;}
    push(id,l,r);
    int m=(l+r)>>1;
    if(L<=m)add(id<<1,l,m,L,R,v);
    if(R>m)add(id<<1|1,m+1,r,L,R,v);
    pull(id);
}

ll maxq(int id,int l,int r,int L,int R){
    if(L<=l&&r<=R)return tree[id];
    push(id,l,r);
    int m=(l+r)>>1; ll res=LLONG_MIN;
    if(L<=m)res=max(res,maxq(id<<1,l,m,L,R));
    if(R>m)res=max(res,maxq(id<<1|1,m+1,r,L,R));
    return res;
}

int main(){
    ios::sync_with_stdio(0);cin.tie(0);
    int n;cin>>n;
    vector<ll>a(n+1);
    for(int i=1;i<=n;i++)cin>>a[i];
    init(n);
    build(1,1,n,a);
    int q;cin>>q;
    while(q--){
        int op;cin>>op;
        if(op==1){
            int L,R;ll v;cin>>L>>R>>v;
            add(1,1,n,L,R,v);
        }else{
            int L,R;cin>>L>>R;
            cout<<maxq(1,1,n,L,R)<<"\n";
        }
    }
}