#include<bits/stdc++.h>
#define ll long long
using namespace std;
unsigned ll a[1000001],t[1000001 << 2],ans[1000001 << 2],n,m,o,t2[1000001 << 2];
inline ll lc(ll x){//left chrildren 左儿子
return x << 1;
}
inline ll rc(ll x){//right chrildren 右儿子
return x << 1 | 1;//二进制位左移一位代表着数值*2,而如果左移完之后再或上1,由于左移完之后最后一位二进制位上一定会是0,所以|1等价于+1。
}
inline void push_up(ll p){// 向上不断维护区间操作
ans[p] = (ans[lc(p)] + ans[rc(p)]) % o;
}
inline void push_up1(ll p){
ans[p] = ans[lc(p)] + ans[rc(p)];
}
void build(ll p,ll l,ll r){
t[p] = 0;//加法懒惰数组
t2[p] = 1;//乘法懒惰数组
if(l == r){
ans[p] = a[l];
return ;
}
ll mid = (l + r) >> 1;
build(lc(p),l,mid);
build(rc(p),mid + 1,r);
push_up(p);//维护
ans[p] %= o;
return ;
}
inline void f(ll p,ll l,ll r,ll k){//我们可以认识到,f函数的唯一目的,就是记录当前节点所代表的区间,然后区间加
t[p] = t[p] + k;
ans[p] = ans[p] + (r - l + 1) * k;
//cout << ans[p] << "aaa" << endl;
//由于是这个区间统一改变,所以ans数组要加元素个数次啦
}
inline void push_down(ll p,ll l,ll r){//每次更新两个子节点,然后继续传递
ll mid = (l + r) >> 1;
ans[lc(p)] = (ans[lc(p)] * t2[p] + t[p] * (mid - l + 1)) % o;//向左儿子传结果
ans[rc(p)] = (ans[rc(p)] * t2[p] + t[p] * (r - (mid + 1) + 1)) % o;//向右儿子传结果
t2[lc(p)] = (t2[lc(p)] * t2[p]) % o;//向左儿子传乘法tag
t2[rc(p)] = (t2[rc(p)] * t2[p]) % o;//向右儿子传乘法tag
t[lc(p)] = (t[lc(p)] * t2[p] + t[p]) % o;//向左儿子传加法tag
t[rc(p)] = (t[rc(p)] * t2[p] + t[p]) % o;//向右儿子传加法tag
t[p] = 0;
t2[p] = 1;
}
inline void upmul(ll nl,ll nr,ll l,ll r,ll p,ll k){ //l,r为要修改的区间,nl,nr,p为当前节点所存储的区间以及节点的编号 ,区间乘法
if(nl <= l && r <= nr){
ans[p] = (ans[p] * k) % o;
t2[p] = (t2[p] * k) % o;
t[p] = (t[p] * k) % o;
return;
}
push_down(p,l,r);
ll mid = (l + r) >> 1;
if(nl <= mid){
upmul(nl,nr,l,mid,lc(p),k);
}
if(nr > mid){
upmul(nl,nr,mid + 1,r,rc(p),k);
}
push_up(p);
}
inline void upadd(ll nl,ll nr,ll l,ll r,ll p,ll k){ //l,r为要修改的区间,nl,nr,p为当前节点所存储的区间以及节点的编号 ,区间加法
if(nl <= l && r <= nr){
ans[p] += (r - l + 1) * k;
ans[p] %= o;
t[p] += k;
t[p] %= o;
return;
}
push_down(p,l,r);
ll mid = (l + r) >> 1;
if(nl <= mid){
upadd(nl,nr,l,mid,lc(p),k);
}
if(nr > mid){
upadd(nl,nr,mid + 1,r,rc(p),k);
}
push_up(p);
}
ll query(ll q_x,ll q_y,ll l,ll r,ll p){//区间求和,[q_x,q_y]为目标区间,[l,r]为当前区间 ,小心函数内部变量重复
ll sum = 0;
if(q_x <= l && r <= q_y){
return ans[p];
}
ll mid = (l + r) >> 1;
push_down(p,l,r);
if(q_x <= mid){
sum += query(q_x,q_y,l,mid,lc(p));
sum %= o;
}
if(q_y > mid){
sum += query(q_x,q_y,mid + 1,r,rc(p));
sum %= o;
}
sum %= o;
return sum;
}
int main(){
cin >> n >> m >> o;
for(int i = 1;i <= n;i++){
scanf("%lld",&a[i]);
}
build(1,1,n);
for(int i = 1;i <= m;i++){
ll zz = 0,xx = 0,yy = 0,nm = 0;
scanf("%lld",&zz);
if(zz == 2){
scanf("%lld %lld %lld",&xx,&yy,&nm);
upadd(xx,yy,1,n,1,nm);
}else if(zz == 3){
scanf("%lld %lld",&xx,&yy);
printf("%lld\n",query(xx,yy,1,n,1));
}else if(zz == 1){
scanf("%lld %lld %lld",&xx,&yy,&nm);
upmul(xx,yy,1,n,1,nm);
}
}
return 0;
}
线段树模板
最新推荐文章于 2024-07-10 16:47:58 发布