#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e6+10;
int n,q,a[N];
struct segment_tree{
int mx,lazy_sign,lazy_add;
bool issign;
}t[N<<2];
void build(int p,int l,int r){
t[p].issign=false;
t[p].mx=-1e9;
if(l==r){
t[p].mx=a[l];
return;
}
int lson=p<<1,rson=p<<1|1,mid=l+(r-l)/2;
build(lson,l,mid);
build(rson,mid+1,r);
t[p].mx=max(t[lson].mx,t[rson].mx);
}
void push_down(int p){
int lson=p<<1,rson=p<<1|1;
if(t[p].issign){
int val=t[p].lazy_sign;
t[lson].issign=true;
t[rson].issign=true;
t[lson].lazy_sign=val;
t[rson].lazy_sign=val;
t[lson].lazy_add=0;
t[rson].lazy_add=0;
t[lson].mx=val;
t[rson].mx=val;
t[p].issign=false;
}
if(t[p].lazy_add!=0){
int val=t[p].lazy_add;
t[lson].lazy_add+=val;
t[rson].lazy_add+=val;
t[lson].mx+=val;
t[rson].mx+=val;
t[p].lazy_add=0;
}
}
void update(int p,int l,int r,int ul,int ur,int val,bool flag){
if(ul<=l&&r<=ur){
if(!flag){
t[p].mx=val;
t[p].lazy_sign=val;
t[p].issign=true;
}else{
t[p].mx+=val;
t[p].lazy_add+=val;
}
return;
}
push_down(p);
int lson=p<<1,rson=p<<1|1,mid=l+(r-l)/2;
if(ul<=mid) update(lson,l,mid,ul,ur,val,flag);
if(ur>mid) update(rson,mid+1,r,ul,ur,val,flag);
t[p].mx=max(t[lson].mx,t[rson].mx);
}
int query(int p,int l,int r,int ql,int qr){
if(ql<=l&&r<=qr) return t[p].mx;
push_down(p);
int lson=p<<1,rson=p<<1|1,mid=l+(r-l)/2;
int res=0;
if(ql<=mid) res=max(res,query(lson,l,mid,ql,qr));
if(qr>mid) res=max(res,query(rson,mid+1,r,ql,qr));
return res;
}
signed main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>n>>q;
for(int i=1;i<=n;i++) cin>>a[i];
build(1,1,n);
while(q--){
int op,l,r,x; cin>>op>>l>>r;
if(op==1){
cin>>x; update(1,1,n,l,r,x,false);
}else if(op==2){
cin>>x; update(1,1,n,l,r,x,true);
}else cout<<query(1,1,n,l,r)<<'\n';
}
return 0;
}