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




 

以下是 C++ 实现的线段树模板代码,支持区间修改、区间求和以及懒惰传播(Lazy Propagation)。此代码综合了已有的参考资料并进行了优化。 ### 线段树模板代码 ```cpp #include <bits/stdc++.h> using namespace std; const int MAXN = 1e5 + 10; int n; // 表示序列长度 long long a[MAXN]; // 原始数组 struct Node { int l, r; // 当前节点覆盖的区间 [l, r] long long sum; // 当前区间的总和 long long lazy; // 懒标记 (用于延迟更新) } tree[MAXN << 2]; // 开四倍空间存储线段树节点 // 获取左孩子节点编号 inline int leftChild(int p) { return p << 1; } // 获取右孩子节点编号 inline int rightChild(int p) { return p << 1 | 1; } // 构建线段树 void buildTree(int p, int l, int r) { tree[p].l = l, tree[p].r = r; tree[p].lazy = 0; // 初始化懒标记为0 if (l == r) { // 如果到达叶子节点,则直接赋值 tree[p].sum = a[l]; return; } int mid = (l + r) >> 1; buildTree(leftChild(p), l, mid); // 构建左子树 buildTree(rightChild(p), mid + 1, r); // 构建右子树 tree[p].sum = tree[leftChild(p)].sum + tree[rightChild(p)].sum; // 合并左右子树的结果[^1] } // 下传懒标记 void pushDown(int p) { if (tree[p].lazy != 0) { tree[leftChild(p)].lazy += tree[p].lazy; tree[rightChild(p)].lazy += tree[p].lazy; tree[leftChild(p)].sum += (long long)(tree[leftChild(p)].r - tree[leftChild(p)].l + 1) * tree[p].lazy; tree[rightChild(p)].sum += (long long)(tree[rightChild(p)].r - tree[rightChild(p)].l + 1) * tree[p].lazy; tree[p].lazy = 0; // 清除当前节点的懒标记 } } // 更新区间 [ql, qr] 的值加上 val void updateRange(int p, int ql, int qr, long long val) { if (ql <= tree[p].l && tree[p].r <= qr) { // 完全包含的情况 tree[p].sum += (long long)(tree[p].r - tree[p].l + 1) * val; tree[p].lazy += val; return; } pushDown(p); int mid = (tree[p].l + tree[p].r) >> 1; if (ql <= mid) updateRange(leftChild(p), ql, qr, val); // 修改左子树 if (qr > mid) updateRange(rightChild(p), ql, qr, val); // 修改右子树 tree[p].sum = tree[leftChild(p)].sum + tree[rightChild(p)].sum; // 合并结果[^2] } // 查询区间 [ql, qr] 的总和 long long querySum(int p, int ql, int qr) { if (ql <= tree[p].l && tree[p].r <= qr) { // 完全包含的情况下直接返回 return tree[p].sum; } pushDown(p); int mid = (tree[p].l + tree[p].r) >> 1; long long res = 0; if (ql <= mid) res += querySum(leftChild(p), ql, qr); // 查询左子树 if (qr > mid) res += querySum(rightChild(p), ql, qr); // 查询右子树 return res; // 返回最终结果[^3] } ``` --- #### 使用说明 1. **初始化**:通过 `n` 设置数组大小,并填充原始数据至 `a[]` 数组。 2. **构建线段树**:调用 `buildTree(1, 1, n)` 来完成线段树的构建。 3. **区间更新**:调用 `updateRange(1, l, r, value)` 对 `[l, r]` 范围内的每个元素增加 `value`。 4. **区间查询**:调用 `querySum(1, l, r)` 计算 `[l, r]` 范围内的总和。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值