【AcWing 1277. 维护序列】线段树双懒标记

7 篇文章 0 订阅
6 篇文章 0 订阅

题目链接

题意:

老师交给小可可一个维护数列的任务,现在小可可希望你来帮他完成。

有长为 N 的数列,不妨设为 a1,a2,…,aN。

有如下三种操作形式:

把数列中的一段数全部乘一个值;
把数列中的一段数全部加一个值;
询问数列中的一段数的和,由于答案可能很大,你只需输出这个数模 P 的值。

分析:

首先想到的就是用两个懒标记做了,一个懒标记对应加法懒标记,一个对应乘法懒标记,然后一开始做的时候是直接进行的最基础的线段树区间更新和区间查询,但是就出现了问题,后来听了y老师的讲解,明白了,假如这个序列的乘法懒标记是mul,加法懒标记是add,然后这个序列的sum应该是summul+add,假如它再乘以一个数mul1呢,那么很显然结果是summulmul1+addmul1,所以说当乘法懒标记更新的时候一定不要忘了更新加法懒标记!!!,加法懒标记更新的时候就无所谓了,因为不涉及到乘法懒标记,下面请看代码,比较长qaq:

#include<bits/stdc++.h>
#define ll (k<<1)
#define rr (k<<1|1)
#define Mid (tr[k].l + tr[k].r >> 1)
#define Len(k) (tr[k].r - tr[k].l + 1)
#define int long long
using namespace std;
const int N = 100010;
typedef long long LL;
int n,mod;
struct node{
    int l,r;
    LL sum,lazy1,lazy2;
}tr[N<<2];
void pu(int k){
    tr[k].sum = (tr[ll].sum + tr[rr].sum) % mod;
}
void build(int k,int l,int r){
    tr[k] = {l,r,0,1,0};
    if(l == r){
        scanf("%lld",&tr[k].sum);
        return;
    }
    build(ll,l,Mid);build(rr,Mid+1,r);
    pu(k);
}
void pd1(int k){
    if(tr[k].lazy1 != 1){
        LL lz = tr[k].lazy1;tr[k].lazy1 = 1;
        tr[ll].lazy1 = (tr[ll].lazy1 * lz) % mod;
        tr[ll].lazy2 = (tr[ll].lazy2 * lz) % mod;
        tr[rr].lazy1 = (tr[rr].lazy1 * lz) % mod;
        tr[rr].lazy2 = (tr[rr].lazy2 * lz) % mod;
        tr[ll].sum = (tr[ll].sum * lz) % mod;
        tr[rr].sum = (tr[rr].sum * lz) % mod;
    }
}
void pd2(int k){
    if(tr[k].lazy2){
        LL lz = tr[k].lazy2;tr[k].lazy2 = 0;
        tr[ll].lazy2 = (tr[ll].lazy2 + lz) % mod;
        tr[rr].lazy2 = (tr[rr].lazy2 + lz) % mod;
        tr[ll].sum = (tr[ll].sum + lz*Len(ll)%mod) % mod;
        tr[rr].sum = (tr[rr].sum + lz*Len(rr)%mod) % mod;
    }
}
void update(int op,int k,int l,int r,int val){ 
    if(tr[k].l >= l && tr[k].r <= r){
        if(op == 1){
            tr[k].lazy1 = (tr[k].lazy1 * val) % mod; 
            tr[k].lazy2 = (tr[k].lazy2 * val) % mod;
            tr[k].sum = (tr[k].sum * val) % mod;
        }
        else{
            tr[k].lazy2 = (tr[k].lazy2 + val) % mod; 
            tr[k].sum = (tr[k].sum + val*Len(k) % mod) % mod;
        }
        return;
    }
    pd1(k);
    pd2(k);
    if(l <= Mid) update(op,ll,l,r,val);
    if(r > Mid) update(op,rr,l,r,val);
    pu(k);
}
LL query(int k,int l,int r){
    if(tr[k].l >= l && tr[k].r <= r) return tr[k].sum;
    pd1(k);
    pd2(k);
    LL ans = 0;
    if(l <= Mid) ans += query(ll,l,r);
    if(r > Mid) ans = (ans + query(rr,l,r)) % mod;
    return ans;
} 
signed main(){
    scanf("%lld%lld",&n,&mod);
    build(1,1,n);
    int m;
    scanf("%lld",&m);
    while(m--){
        int op,l,r,x;
        scanf("%lld%lld%lld",&op,&l,&r);
        if(op == 1){
            scanf("%lld",&x);
            update(1,1,l,r,x);
        }
        else if(op == 2){
            scanf("%lld",&x);
            update(2,1,l,r,x);
        }
        else{
            LL ans = query(1,l,r);
            printf("%lld\n",ans);
        }
    }
    system("pause");
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

宇智波一打七~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值