[题解]洛谷线段树2
原题链
算法:线段树的懒标记之乘法与加法
思路:
对于懒标记(重难点),我们采用先乘后加的方法。图片
代码
#include <bits/stdc++.h>
using namespace std;
struct node{//线段树定义
long long w,ad,mm;
int l,r;
}tree[800003];
long long n,m;
long long p;
void build(long long k,long long l,long long r){//建树
tree[k].l = l;
tree[k].r = r;
tree[k].mm = 1;//乘法标记初始值要为1,因为$0 × 0 = 0$为无效操作
if(l == r){//此节点为叶子节点
cin>>tree[k].w;//输入
tree[k].w %= p;
return;
}
long long mid = (tree[k].l + tree[k].r) / 2;
build(k * 2,l,mid);//构建左子树
build(k * 2 + 1,mid + 1,r);//构建右子树
tree[k].w = (tree[k * 2].w + tree[k * 2 + 1].w) % p;//上传
}
void down(long long k){//懒标记下移
//参考上面图片可知乘法标记会影响加法标记而加法标记不会影响乘法标记
tree[k << 1].w = (tree[k << 1].w * tree[k].mm + tree[k].ad * (tree[k << 1].r - tree[k << 1].l + 1)) % p;//先将区间和与乘法标记相乘,再处理加法标记
tree[k << 1 | 1].w = (tree[k << 1 | 1].w * tree[k].mm + tree[k].ad * (tree[k << 1 | 1].r - tree[k << 1 | 1].l + 1)) % p;//与上同理
tree[k << 1].mm = (tree[k << 1].mm * tree[k].mm) % p;//处理乘法标记的操作是将父节点的乘法标记乘当前节点的乘法标记
tree[k << 1 | 1].mm = (tree[k << 1 | 1].mm * tree[k].mm) % p;//同理
tree[k << 1].ad = (tree[k << 1].ad * tree[k].mm + tree[k].ad ) % p;//处理加法标记的操作为将当前加法标记加上此加法标记与父节点乘法标记的积
tree[k << 1 | 1].ad = (tree[k << 1 | 1].ad * tree[k].mm + tree[k].ad) % p;//同理
tree[k].mm = 1; tree[k].ad = 0;//用过了的懒标记不再重复使用
}
void change_part_mu(long long k,long long x,long long y,long long w){//处理乘法
if(tree[k].l >= x && tree[k].r <= y){//判断此节点在处理范围内
tree[k].mm = (tree[k].mm * w) % p;
tree[k].w = (tree[k].w * w) % p;
tree[k].ad = (w * tree[k].ad) % p;
return;
}
down(k);
long long mid = (tree[k].l + tree[k].r) / 2;
if(x <= mid)
change_part_mu(k * 2,x,y,w);
if(y > mid)
change_part_mu(k * 2 + 1,x,y,w);
tree[k].w = (tree[k * 2].w + tree[k * 2 + 1].w) % p;
}
void change_part_add(long long k,long long x,long long y,long long w){//处理加法
if(tree[k].l >= x && tree[k].r <= y){
tree[k].w = tree[k].w + w * (tree[k].r - tree[k].l + 1);
tree[k].ad = (tree[k].ad + w) % p;
return ;
}
down(k);
long long mid = (tree[k].l + tree[k].r) / 2;
if(x <= mid)
change_part_add(k * 2,x,y,w);
if(y > mid)
change_part_add(k * 2 + 1,x,y,w);
tree[k].w = (tree[k * 2].w + tree[k * 2 + 1].w) % p;
}
long long find_part(long long k,long long x,long long y){//区间求和
if(tree[k].l >= x && tree[k].r <= y)
return tree[k].w;//判断此节点在统计范围内,返回这个节点的值,累加如ans
down(k);
long long mid = (tree[k].l + tree[k].r) / 2;
long long ans = 0;//只在当前这一次函数中被改变,当在函数内进行函数调用时ans归0不影响上一层的ans
if(x <= mid)
ans = (ans + find_part(k * 2,x,y)) % p;
if(y > mid)
ans = (ans + find_part(k * 2 + 1,x,y)) % p;
return ans % p;
}
int main(){
cin>>n>>m>>p;
build(1,1,n);
for(int i = 1;i <= m;i++){
int qq;
cin>>qq;
if(qq == 1){
int a,b,w;
cin>>a>>b>>w;
change_part_mu(1,a,b,w);
}
else if(qq == 2){
int a,b,w;
cin>>a>>b>>w;
change_part_add(1,a,b,w);
}
else {
int a,b;
cin>>a>>b;
cout<<find_part(1,a,b) % p<<endl;
}
}
return 0;
}