洛谷 P3373 【模板】线段树 2

题意

区间加法 , 区间乘法 , 区间查询

思路

第一个问题是解决加法和乘法之间的懒标记关系

如果一个地方有加法懒标记 , 代表这个区域内所有的元素都要加上某个数 , 设该数为 \(add\) , 区域范围为 \([ l , r ]\)

就是说 \(LzyAdd_{x,y} = add\) 就是 \(\forall i , a_i = a_i + add\) , 且 $ i>=l , i<= r$ , 只是还未将其算出来

乘法同理

显然加法和加法,乘法和乘法不用考虑(以下加内容省略区间长度)

只考虑以下两种情况:

  1. 当前有加法标记 , 正在设置乘法懒标记

  2. 当前有乘法标记 , 正在设置加法懒标记

考虑push_down部分执行先加后乘 , 那么1情况没问题 ;

但是2情况 , 如果还是按照先加后乘 , 那么会犯错 \((a + b) \times c = a \times c + b\) 的错误

如果我们坚持要先加后乘 , 需要把加法部分提前除对应的系数 , 使得它和乘法项相乘后为原始加数

但是我们是不喜欢除法的 , 这个时候试想一下先乘后加:

2情况满足

1情况 , 我们将add数据乘上添加的乘法懒标记值

问题解决

第二个问题是解决push_down

push_down的操作是把当前层的add , mul 信息传递给下一层

下一层有三个值 , val , add , mul

下一层的val值变化已经讲过了 , 采用先乘后加即可

下一层的mul值肯定是一路乘下去的

下一层的add比较难想(对我来说) , 可以认为它类似val值 , 只不过表示改变量 , 且没有乘区间长度

所以也应该是乘后加

也可以想 , push_down的过程分乘,加两个阶段 , 当mul被上层乘时 , add要跟着mul一起乘 , 所以也是先乘后加

代码

#include<bits/stdc++.h>
using namespace std;
#define int long long int
inline int read() {
    int ans = 0, f = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9') {
        if (ch == '-')f = -1;
        ch = getchar();
    }
    while (ch <= '9' && ch >= '0') {
        ans = ans * 10 + ch - '0';
        ch = getchar();
    }
    return ans * f;
}
const int N = 1e5+10;
int a[N];
int n,m,qu;
struct Node {
    int l,r,val,add,mul;
};
Node tr[N<<2];
void push_up(int p) {
    tr[p].val  = tr[p*2].val + tr[p*2+1].val;
}
void push_down(int p) {
    tr[p*2].val = ((tr[p*2].val * tr[p].mul)%m + tr[p].add * (tr[p*2].r - tr[p*2].l+1))%m;
    tr[p*2+1].val = ((tr[p*2+1].val * tr[p].mul)%m + tr[p].add * (tr[p*2+1].r - tr[p*2+1].l + 1))%m;
    tr[p*2].add = (tr[p*2].add * tr[p].mul%m + tr[p].add) % m;
    tr[p*2+1].add =(tr[p*2+1].add * tr[p].mul%m +  tr[p].add) % m;
    tr[p*2].mul = (tr[p*2].mul * tr[p].mul) % m;
    tr[p*2+1].mul = (tr[p*2+1].mul * tr[p].mul) % m;
    tr[p].mul = 1;
    tr[p].add = 0;
}
void build(int p,int l, int r) {
    tr[p] = {l,r,a[l],0,1};
    if (l==r)return ;
    int m  = l + r >> 1;
    build(p*2,l,m);
    build(p*2+1,m+1,r);
    push_up(p);
}
void add(int p,int l,int r,int k) {
    if (tr[p].l>=l && tr[p].r <= r) {
        tr[p].add =(tr[p].add + k)%m;
        tr[p].val =(tr[p].val+ k * (tr[p].r - tr[p].l + 1)%m )%m;
        return ;
    }
    push_down(p);
    int m  = tr[p].l + tr[p].r >> 1;
    if (m >= l) {
        add(p*2,l,r,k);
    }
    if (r>m) {
        add(p*2+1,l,r,k);
    }
    push_up(p);
}
void multi(int p,int l,int r,int k) {
    if (tr[p].l >= l && tr[p].r <= r) {
        tr[p].mul *= k;
        tr[p].mul %=m;
        tr[p].val *= k;
        tr[p].val %= m;
        tr[p].add *= k;
        tr[p].add %= m;
        return ;
    }
    push_down(p);
    int m = tr[p].l + tr[p].r >> 1;
    if (m >= l) {
        multi(p*2,l,r,k);
    }
    if (r>m) {
        multi(p*2+1,l,r,k);
    }
    push_up(p);
}
int q(int p,int l,int r) {
    if (tr[p].l >= l && tr[p].r <= r) {
        return tr[p].val;
    }
    push_down(p);
    int m = tr[p].l + tr[p].r >> 1;
    int res = 0;
    if (l <=m)
        res += q(p*2,l,r);
    if (r >m)
        res += q(p*2+1,l,r);
    return res;
}
signed main() {
    n =read(), qu= read(),m=read();
    for (int i =1; i<= n; i++){a[i] =read();}
    build(1,1,n);
    int x,y,k;
    while (qu--) {
        int op = read();
        if (op==1) {
            x=read(),y=read(),k=read();
            multi(1,x,y,k);
        }
        else if (op == 2) {
            x=read(),y=read(),k=read();
            add(1,x,y,k);
        }
        else {
            x= read(),y=read();
            cout<<q(1,x,y)%m<<"\n";
        }
    }
    return 0;
}
posted @ 2025-04-02 11:03  Guaninf  阅读(14)  评论(0)    收藏  举报