P3373 【模板】线段树 2

本文详细解析了线段树如何同时处理区间加法和乘法操作,重点介绍了在下推懒标记时,如何确保加法和乘法操作的一致性和准确性,通过实例代码展示了具体的实现细节。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

    线段树同时实现区间加法和乘法,因为加法乘法都涉及到了动态修改,因此我们需要两个lazy标记,我们记为add,mul,但是在pushdown的时候就会出现一个问题,乘法对之前做过的加法标记是有影响的,如果先下推加法标记,那么就忽略乘法对结果的影响,导致错误的答案

    当我们需要将标记下推的时候,正确的操作应该是将当前区间的add也乘上mul,下推保证结果正确,而在添加mul标记的时候之前这个位置所拥有的add标记也要乘上mul,这样保证在做乘法操作的时候,加法保持正确。加法就按原本的操作,无需改动。

代码如下:

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn = 1e6 + 10;
struct SegmentTree { int l,r; LL sum,mul = 1,add;}tree[maxn<<2];
LL num[maxn], mod;
void pushup(int id){
    tree[id].sum = (tree[id<<1].sum + tree[id<<1|1].sum) % mod;
}
void pushdown(int id){
    if (tree[id].mul != 1 || tree[id].add){
        int l = tree[id].l,r = tree[id].r,mid = (tree[id].l + tree[id].r) >> 1;
        tree[id<<1].mul = (tree[id<<1].mul*tree[id].mul) % mod;
        tree[id<<1|1].mul = (tree[id<<1|1].mul*tree[id].mul) % mod;
        tree[id<<1].add = (tree[id<<1].add*tree[id].mul) % mod;
        tree[id<<1|1].add = (tree[id<<1|1].add*tree[id].mul) % mod;
        tree[id<<1].sum = (tree[id<<1].sum*tree[id].mul) % mod;
        tree[id<<1|1].sum = (tree[id<<1|1].sum*tree[id].mul) % mod;
        tree[id].mul = 1;
        tree[id<<1].add = (tree[id<<1].add+tree[id].add) % mod;
        tree[id<<1|1].add = (tree[id<<1|1].add+tree[id].add) % mod;
        tree[id<<1].sum = (tree[id<<1].sum+(mid-l+1)*tree[id].add) % mod;
        tree[id<<1|1].sum = (tree[id<<1|1].sum+(r-mid)*tree[id].add) % mod;
        tree[id].add = 0;
    }
}
void build(int id,int l,int r){
    tree[id].l = l;
    tree[id].r = r;
    if (l == r) {
        tree[id].sum = num[l];
        return;
    }
    int mid = (l + r) >> 1;
    build(id<<1, l, mid);
    build(id<<1|1, mid+1, r);
    pushup(id);
}
void add(int id,int ql,int qr,int c){
    int l = tree[id].l,r = tree[id].r;
    if (ql <= l && r <= qr){
        tree[id].add = (tree[id].add + c) % mod;
        tree[id].sum = (tree[id].sum + (r-l+1)*c) % mod;
        return;
    }
    int mid = (r + l) >> 1;
    pushdown(id);
    if (ql <= mid) add(id<<1, ql, qr, c);
    if (qr > mid) add(id<<1|1, ql, qr, c);
    pushup(id);
}
void mul(int id,int ql,int qr,int c){
    int l = tree[id].l,r = tree[id].r;
    if (ql <= l && r <= qr){
        tree[id].mul = tree[id].mul*c % mod;
        tree[id].add = tree[id].add*c % mod;
        tree[id].sum = tree[id].sum*c % mod;
        return;
    }
    int mid = (l + r) >> 1;
    pushdown(id);
    if (ql <= mid) mul(id<<1, ql, qr, c);
    if (qr > mid) mul(id<<1|1, ql, qr, c);
    pushup(id);
}
LL query1(int id,int ql,int qr){
    int l = tree[id].l,r = tree[id].r;
    if (ql <= l && r <= qr) return tree[id].sum;
    pushdown(id);
    int mid = (l + r) >> 1;
    LL sum = 0;
    if (mid >= qr) sum = (sum + query1(id<<1, ql, qr)) % mod;
    else if (ql >= mid+1) sum = (sum + query1(id<<1|1, ql, qr)) % mod;
    else{
        sum = (sum + query1(id<<1, ql, qr)) % mod;
        sum = (sum + query1(id<<1|1, ql, qr)) % mod;
    }
    return sum%mod;
}
int main(){
   // freopen("/Users/chutong/data.txt", "r", stdin);
    int n,m,x,y,k;
    scanf("%d%d%lld",&n,&m,&mod);
    for (int i=1; i<=n; i++)
        scanf("%lld",&num[i]);
    build(1, 1, n);
    int opt;
    for (int i=1; i<=m; i++) {
        scanf("%d%d%d",&opt,&x,&y);
        if (opt == 1){
            scanf("%d",&k);
            mul(1, x, y, k);
        }else if (opt == 2){
            scanf("%d",&k);
            add(1, x, y, k);
        }else{
            printf("%lld\n",query1(1, x, y));
        }
    }
    return 0;
}




 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值