#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
#include <vector>
#include <set>
#include <map>
#include <unordered_map>
#include <cstdio>
#include <cstring>
#include <queue>
#include <cstdlib>
#include <algorithm>
#include <list>
#include <string>
#include <cmath>
#include <bitset>
#include <stack>
#include <unordered_set>
#include <math.h>
#include <functional>
#include<unordered_map>
#define ull unsigned long long
#define ll long long
#define pii pair<ll,ll>
#define ft first
#define sd second
#define cy cout<<"YES"<<endl
#define cn cout<<"NO"<<endl
#define forn(i,n) for(ll (i)=0;(i)<(n);(i)++)
#define forne(i,n) for(ll (i)=1;(i)<=(n);(i)++)
#define rforn(i,n) for(ll (i)=(n-1);i>=0;i--)
#define rforne(i,n) for(ll (i)=n;i>=1;i--)
#define vv vector
#define inf 1e9
const int N = 4e5 + 100;
const int mod = 1e9 + 7;
ll T;
class Segment_tree {
struct node {
ll l, r, add,mx,upd;
};
vv<node>tr;
vv<ll>w;
#define lc p<<1
#define rc (p<<1)|1
#define LEN(x) (tr[(x)].r - tr[(x)].l + 1)
#define MID(x) (tr[(x)].l + tr[(x)].r) >> 1
#define trpl tr[p].l
#define trpr tr[p].r
#define trps tr[p].sum
#define trpd tr[p].add
public:
void init(int n) {
tr = vv<node>(4 * n + 3);
w = vv<ll>(n + 1);
forne(i, n)
cin >> w[i];
build(1, 1, n);
}
void pushup(int p) {
tr[p].mx = max(tr[lc].mx, tr[rc].mx);
}
void build(int p, int l, int r) {
tr[p] = { l,r,0,w[l],0 };
if (l == r) return;
int m = MID(p);
build(lc, l, m); build(rc, m + 1, r);
pushup(p);
}
ll query(int p, int l, int r) {
ll mx = -1e9-100;
if (l <= trpl && trpr <= r)
return tr[p].mx;
int m = MID(p); pushdown(p);
if (l <= m) mx=max(mx,query(lc, l, r));
if (r > m) mx=max(mx,query(rc, l, r));
return mx;
}
void pushdown(int p) {
if (tr[p].upd) {
tr[p].mx = tr[p].upd;
tr[lc].upd = tr[p].upd;
tr[rc].upd = tr[p].upd;
tr[lc].add =0;
tr[rc].add =0;
tr[p].upd = 0;
}
if (tr[p].add) {
tr[lc].mx += tr[p].add;
tr[rc].mx += tr[p].add;
tr[lc].add += trpd;
tr[rc].add += trpd;
tr[p].add = 0;
}
}
void update(int p, int l, int r, int k) {
if (l <= trpl && trpr <= r) {
if (tr[p].upd != 0)
tr[p].upd += k;
else
trpd += k;
tr[p].mx += k;
return;
}
int m = MID(p);
pushdown(p);
if (l <= m) update(lc, l, r, k);
if (r > m) update(rc, l, r, k);
pushup(p);
}
void update_all(int p, int l, int r, int x) {
if (l <= trpl && trpr <= r) {
tr[p].upd = x;
tr[p].add = 0;
tr[p].mx = x;
return;
}
int m = MID(p);
pushdown(p);
if (l <= m) update_all(lc, l, r, x);
if (r > m) update_all(rc, l, r, x);
pushup(p);
}
};
void solve() {
int n, q;
cin >> n >> q;
Segment_tree fuction;
fuction.init(n);
while (q--) {
int ch; cin >> ch;
if (ch == 1) {
int l, r, x; cin >> l >> r >> x;
fuction.update_all(1, l, r, x);
}
else if(ch==2) {
int l, r, x; cin >> l >> r >> x;
fuction.update(1,l, r, x);
}
else {
int l, r; cin >> l >> r;
cout << fuction.query(1, l, r) << endl;
}
}
}
int main() {
T = 1;
while (T--)
solve();
}