洛谷 P3373 【模板】线段树 2
题意
区间加法 , 区间乘法 , 区间查询
思路
第一个问题是解决加法和乘法之间的懒标记关系
如果一个地方有加法懒标记 , 代表这个区域内所有的元素都要加上某个数 , 设该数为 \(add\) , 区域范围为 \([ l , r ]\)
就是说 \(LzyAdd_{x,y} = add\) 就是 \(\forall i , a_i = a_i + add\) , 且 $ i>=l , i<= r$ , 只是还未将其算出来
乘法同理
显然加法和加法,乘法和乘法不用考虑(以下加内容省略区间长度)
只考虑以下两种情况:
-
当前有加法标记 , 正在设置乘法懒标记
-
当前有乘法标记 , 正在设置加法懒标记
考虑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;
}

浙公网安备 33010602011771号