线段树标记永久化

线段树的标记永久化

其实线段树的标记永久化是一个非常容易理解的东西,往往我们都会在区间操作时打lazytag,但是在标记下放时会耗费大量的时间,所以我们可以尝试标记永久化,这样我们的就不用下放标记,同时代码也更加简洁,因为我们少了一个pushdown函数,同时出错率也会大大降低。
对于标记永久化,其实和普通线段树比起来,其实差不多

#include<iostream>
#include<cstdio>
#include<cstring>

using namespace std;

typedef long long ll;
int n,m;
ll sum[4000001],tag[4000001]; 


void pushup(int o){
    sum[o]=sum[o<<1]+sum[o<<1|1]; 
}

void build(int o,int l,int r){
    if(l==r){
        scanf("%lld",&sum[o]);
        return;
    }
    int mid=(l+r)>>1;
    build(o<<1,l,mid);
    build(o<<1|1,mid+1,r);
    pushup(o);
}

void update(int o,int l,int r,int x,int y,ll v){
    sum[o]+=((ll)min(r,y)-(ll)max(x,l)+1)*v;
    if(x<=l and r<=y){
        tag[o]+=v;
        return;
    }
    int mid=(l+r)>>1;
    if(x<=mid){
        update(o<<1,l,mid,x,y,v);
    }
    if(y>mid){
        update(o<<1|1,mid+1,r,x,y,v);
    }
}

ll query(int o,int l,int r,ll tg,int x,int y){
    if(x<=l and r<=y){
        return sum[o]+(ll)(min(r,y)-max(x,l)+1)*(tg);
    }
    int mid=(l+r)>>1;
    ll ret=0;
    if(x<=mid){
        ret+=query(o<<1,l,mid,tg+tag[o],x,y);
    }
    if(y>mid){
        ret+=query(o<<1|1,mid+1,r,tg+tag[o],x,y);
    }
    return ret;
}

int main(){
    scanf("%d%d",&n,&m);
    build(1,1,n);
    for(int i=1;i<=m;i++){
        int opt;
        scanf("%d",&opt);
        if(opt==1){
            int x,y;
            ll z;
            scanf("%d%d%lld",&x,&y,&z);
            update(1,1,n,x,y,z);
        }else{
            int x,y;
            scanf("%d%d",&x,&y);
            printf("%lld\n",query(1,1,n,0,x,y));
        }
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/ezoihy/p/9369730.html