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

题目链接

题意:

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

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

有如下三种操作形式:

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

分析:

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

#include
#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;
}

你可能感兴趣的:(线段树,学习日记,树,算法,c++)