【算法】线段树

之前由于不会延迟标记一直没写这题。

那么今天就写一个总结来加深印象吧!

首先,延迟标记的作用就是在于当某个区间遭到改变,但又用不上(不用输出)时。将所有先前改变都标记上去,等到要用到了,一次直接改。

#include 
#define maxn 100005
using namespace std;
struct segment_tree{
    int l,r;
    long long v,add;//加一个延迟标记的变量
}t[maxn*4];
long long a[4*maxn];
void build_up(int x,int l,int r){
    int mid=(l+r)/2;
    t[x].l=l;t[x].r=r;
    if (l==r){t[x].v=a[l];return ;}
       build_up(x*2,l,mid);
       build_up(x*2+1,mid+1,r);
       t[x].v=t[x*2].v+t[x*2+1].v;//标准建树
}
void spread(int x){
    if (t[x].add){
       t[x*2].v+=t[x].add*(t[x*2].r-t[x*2].l+1);
       t[x*2+1].v+=t[x].add*(t[x*2+1].r-t[x*2+1].l+1);//更改区间值
       t[x*2].add+=t[x].add;//两个子树加上标记
       t[x*2+1].add+=t[x].add;
       t[x].add=0;//清空当前标记
    }
}
void change(int x,int l,int r,long long d){
     int mid=(t[x].l+t[x].r)/2;
     if (l<=t[x].l&&r>=t[x].r){//若此节点被整个更改区间覆盖
        t[x].v+=d*(t[x].r-t[x].l+1);
        t[x].add+=d;//(接上)则可以直接更改它的值,并加上标记
        return ;
     }
     spread(x);//若没有,则直接向下改
     if (l<=mid) change(x*2,l,r,d);
     if (r>mid) change(x*2+1,l,r,d);
     t[x].v=t[x*2].v+t[x*2+1].v;//这时再改值
}
long long ask(int x,int l,int r){
     if (l<=t[x].l&&r>=t[x].r)return t[x].v;//如果被当前区间覆盖,就可以直接输出值了
     spread(x);//若没有,则向下标记
     int mid=(t[x].l+t[x].r)/2;
     long long val=0;
     if (l<=mid) val+=ask(x*2,l,r);
     if (r>mid) val+=ask(x*2+1,l,r);
     return val;
}
//void merge(int x,int y){
//     spread(t[x].l,t[x].r);
    // if (t[x].l!=t[x].r)
//}
int main(){
    int i,m,n;
    cin>>n>>m;
    for (i=1;i<=n;i++)
        cin>>a[i];
    build_up(1,1,n);
    for (i=1;i<=m;i++){
        int x,y,z;
        long long k;
        cin>>x>>y>>z;
        if (x==1){
           cin>>k;
           change(1,y,z,k);
        }
        else cout<

你可能感兴趣的:(【算法】线段树)