#include<bits/stdc++.h>
using namespace std;
#define IOS ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
#define int long long
#define PII pair<int,int>
#define fi first
#define pb push_back
#define sec second
#define endl '\n'
#define INF 0x3f3f3f3f
const int N=2e5+10;
const int mod = LONG_LONG_MAX ;
//这里没影响,可以根据题目修改
int n,m,k,t ;
int s[N] ;
struct seg{
int v,l,r ;
int ladd,lmul ;
}tr[4*N];
//创建一个seg结构体,储存线段树的内容,分别对应 :当前值,左区间,右区间 ,以及两个懒标记(方便区间乘法和加减)
void pushup(int u){
tr[u].v=tr[u<<1].v%mod+tr[u<<1|1].v%mod;
//这里是把下面的内容更新到上面来
/*
假如我一共两个数据,那一共会有3个区间,分别为:{1,2}{1,1}{2,2}
当一个区间的左右相等(l==r)时,我们会把s[l]赋值给当前的v,然后通过pushup把下面的v值变化赋给当前点
*/
}
void eval(seg &t, int add, int mul){
//当使用乘法修改时add是0,当使用加法修改时,mul是1,所以不用担心影响到数据。
t.v = (t.v * mul + (t.r - t.l + 1) * add) % mod;
//给v值附上变化
t.lmul = t.lmul* mul % mod;
//记录加减乘除的情况,这样用不到的时候就不向下推,节省时间,假设我要给 [4,8]进行修改,我不需要给4到8每个数字进行更改,我只需要给储存4-8的这一段修改,如果调用,这里的懒标记就会向下释放返回正确的值
t.ladd = (t.ladd * mul + add) % mod;
//
}
void pushdown(int u){
//这里就是懒标记向下释放的过程
eval(tr[u<<1],tr[u].ladd,tr[u].lmul);
eval(tr[u<<1|1],tr[u].ladd,tr[u].lmul);
tr[u].ladd=0;
tr[u].lmul=1;
//当前层释放完之后记得清理当前的储存内容,防止一个添加多次增长
}
void build(int u,int l,int r){
if(l==r){
tr[u]={s[l],l,r,0,1};
//如上25-26行的注释
return ;
//这里记得返回,因为这条路已经递归到底了,不返回程序就会一直往下走卡死
}
tr[u]={0,l,r,0,1};
//这里赋予当前的点u初始的v(val)为0
int mid = l + r>>1;
//划分左右子树,l+r>>1 和 (l+r)/2 是一样的
build(u<<1,l,mid);
build(u<<1|1,mid+1,r);
pushup(u);
//下面的数据更新完毕,修改当前的v值
return ;
}
void modifyadd(int u,int add,int l,int r){
if(tr[u].l>=l&&tr[u].r<=r){
eval(tr[u],add,1);
//如果区间符合条件,就直接进行数据更新操作
return ;
//记得 return
}
pushdown(u);
//向下释放上次用懒标记储存的数据,防止重复加减等干扰
int mid = (tr[u].l+tr[u].r)/2;
//记得是当前点u的左右去取mid而不是你要修改的区间的 l,r,我写错好几次了
if(l<=mid){
modifyadd(u<<1,add,l,r);
}
if(r>mid){
modifyadd(u<<1|1,add,l,r);
}
//判断左右两边在不在修改范围之内,如果是就进入修改
pushup(u);
//更新当前点的数据
}
void modifymul(int u,int mul,int l,int r){
// cout << tr[u].l << " " << tr[u].r << endl;
if(tr[u].l>=l&&tr[u].r<=r){
eval(tr[u],0,mul);
return ;
}
pushdown(u);
int mid =tr[u].l+tr[u].r >> 1;
if(l<=mid){
modifymul(u<<1,mul,l,r);
}
if(r>=mid+1){
modifymul(u<<1|1,mul,l,r);
}
pushup(u);
}
//这个是区间乘法,跟上面一样
int query(int u,int l,int r){
if(tr[u].l>=l&&tr[u].r<=r){
return tr[u].v%mod;
//如果整个区间在查询范围之内 ,直接返回v值
}
pushdown(u);
//到这里说明当前区间有部分不是查询范围,所以要继续向左右找,要释放一下懒标记储存的内容
int mid=(tr[u].l+tr[u].r)>>1;
int v=0;
if(l<=mid)v+=query(u<<1,l,r);
if(r>mid)v+=query(u<<1|1,l,r);
return v%mod;
}
signed main(){
cin >> n >> m ;
for(int i=1;i<=n;i++){
cin >> s[i];
}
int op ;
int l,r,v;
build(1,1,n);
//记得建树,建树,建树,建树!!!!!!(我忘记好几次了)
while(m--){
cin >> op ;
if(op==1){
//区间加减
cin >> l >> r >> v;
modifyadd(1,v,l,r);
}else if(op==2){
//区间乘
cin >> l >> r >> v;
modifymul(1,v,l,r);
}else{
//查询
cin >> l >> r ;
cout << query(1,l,r) << endl;
}
}
return 0;
}
关注永雏塔菲喵~~~~关注永雏塔菲谢谢喵~~~~~~~