线段树及其懒标记

懒标记基本原则
1、如果当前区间被完全覆盖在目标区间里,讲这个区间的sum+k*(tree[i].r-tree[i].l+1)

2、如果没有完全覆盖,则先下传懒标记

3、如果这个区间的左儿子和目标区间有交集,那么搜索左儿子

4、如果这个区间的右儿子和目标区间有交集,那么搜索右儿子
代码示例

//洛谷P3372
#include<iostream>
#include<queue>
#include<string>
using namespace std;

typedef long long ll;
const int N=1e5+10;
ll n,m;
ll a[N];
struct Tree{
    ll l,r;
    ll sum; //维护值为区间和
    ll lz;
}tr[N*4];

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

void pushdown(int p){
    //消除懒标记
    if(tr[p].lz){
        tr[p<<1].lz+=tr[p].lz;
        tr[p<<1|1].lz+=tr[p].lz;
        tr[p<<1].sum+=(tr[p<<1].r-tr[p<<1].l+1)*tr[p].lz;
        tr[p<<1|1].sum+=(tr[p<<1|1].r-tr[p<<1|1].l+1)*tr[p].lz;
        tr[p].lz=0;
    }
}

void build(int p,ll l,ll r){
    //建树
    tr[p]={l,r,0,0};
    if(l==r){
        tr[p]={l,r,a[r],0};
        return ;
    }
    int mid=l+r>>1;
    build(p<<1,l,mid);
    build(p<<1|1,mid+1,r);
    pushup(p);
}

void modify(int p,ll l,ll r,ll v){
    //使区间l到r之间每个元素都加上v
    if(tr[p].l>=l&&tr[p].r<=r){
        tr[p].sum+=(tr[p].r-tr[p].l+1)*v;
        tr[p].lz+=v;//懒标记
        return ;
    }
    pushdown(p);//pushdown更新修改后区间的值
    ll mid=tr[p].l+tr[p].r>>1;
    if(l<=mid) modify(p<<1,l,r,v);
    if(r>mid) modify(p<<1|1,l,r,v);
    pushup(p);
}

ll query(int p,ll l,ll r){
    //查询区间l到r之间所有元素的和
    if(tr[p].r<l||tr[p].l>r)
        return 0;

    if(tr[p].l>=l&&tr[p].r<=r)
        return tr[p].sum;

    pushdown(p);
    ll res=0;
    res+=query(p<<1,l,r);
    res+=query(p<<1|1,l,r);
    return res;
}


int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;++i){
        cin>>a[i];
    }
    build(1,1,n);
    while(m--){
        int opt,left,right,k;
        cin>>opt;
        if(opt==1){
            cin>>left>>right>>k;
            modify(1,left,right,k);
        }
        else{
            cin>>left>>right;
            cout<<query(1,left,right)<<endl;
        }
    }
    return 0;
}

P3373 【模板】线段树 2
此题为需要区间乘法操作,进行区间乘法操作时,应将之前的加法标记也要乘以v

#include<iostream>
#include<queue>
#include<string>
using namespace std;

typedef long long ll;
const int N=1e5+10;

ll n,m,pp;
ll a[N];
struct Tree{
    ll l,r;
    ll sum; //维护值为区间和
    ll lz;
    ll lz2;
}tr[N*4];

void pushup(int p){
    tr[p].sum=(tr[p<<1].sum+tr[p<<1|1].sum)%pp;
}

void pushdown(int p){
    //消除懒标记
    Tree &root=tr[p],&left=tr[p<<1],&right=tr[p<<1|1];
    if(root.lz||root.lz2!=1){
        left.lz=(left.lz*root.lz2+root.lz)%pp;
        right.lz=(right.lz*root.lz2+root.lz)%pp;

        left.lz2=left.lz2*root.lz2%pp;
        right.lz2=right.lz2*root.lz2%pp;

        left.sum=(left.sum*(root.lz2%pp)+(left.r-left.l+1)*root.lz)%pp;
        right.sum=(right.sum*root.lz2+(right.r-right.l+1)*root.lz)%pp;

        root.lz=0;
        root.lz2=1;
    }
}

void build(int p,ll l,ll r){
    //建树
    tr[p]={l,r,0,0,1};
    if(l==r){
        tr[p]={l,r,a[r],0,1};
        return ;
    }
    int mid=l+r>>1;
    build(p<<1,l,mid);
    build(p<<1|1,mid+1,r);
    pushup(p);
}

void modify(int p,ll l,ll r,ll v){
    //使区间l到r之间每个元素都加上v
    if(tr[p].l>=l&&tr[p].r<=r){
        tr[p].sum=(tr[p].sum+(tr[p].r-tr[p].l+1)*v%pp)%pp;
        tr[p].lz+=v;//懒标记
        return ;
    }
    pushdown(p);//pushdown更新修改后区间的值
    ll mid=tr[p].l+tr[p].r>>1;
    if(l<=mid) modify(p<<1,l,r,v);
    if(r>mid) modify(p<<1|1,l,r,v);
    pushup(p);
}
void modify2(int p,ll l,ll r,ll v){
    //将区间l到r之间每个数都乘以v
    if(tr[p].l>=l&&tr[p].r<=r){
        tr[p].sum=(tr[p].sum*v)%pp;
        tr[p].lz2=tr[p].lz2*v%pp;
        tr[p].lz=tr[p].lz*v%pp;//如果在乘以v操作之前已经进行过加法操作,则应将加法标记也乘以v
        return ;
    }
    pushdown(p);
    ll mid=tr[p].l+tr[p].r>>1;
    if(l<=mid) modify2(p<<1,l,r,v);
    if(r>mid) modify2(p<<1|1,l,r,v);
    pushup(p);
}

ll query(int p,ll l,ll r){
    //查询区间l到r之间所有元素的和
    if(tr[p].r<l||tr[p].l>r)
        return 0;

    if(tr[p].l>=l&&tr[p].r<=r)
        return tr[p].sum;

    pushdown(p);
    ll res=0;
    res=(res+query(p<<1,l,r))%pp;
    res=(res+query(p<<1|1,l,r))%pp;
    return res;
}


int main()
{
    cin>>n>>m>>pp;
    for(int i=1;i<=n;++i){
        cin>>a[i];
    }
    build(1,1,n);
    while(m--){
        ll x,y,k,opt;
        cin>>opt;
        if(opt==1){
            cin>>x>>y>>k;
            modify2(1,x,y,k);
        }
        else if(opt==2){
            cin>>x>>y>>k;
            modify(1,x,y,k);
        }
        else if(opt==3){
            cin>>x>>y;
            cout<<query(1,x,y)<<endl;
        }
    }
    return 0;
}

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值