令标记为<p,c>表示对这个区间加p后和c取max
再多来两个标记记录两次修改间这个节点所能得到的最大标记是多少
诶写线段树之前真应该把标记加法和节点加法先在草稿纸上写好以后再开始敲
不然写起来很难受
#include <iostream>
#include <cstdio>
#define N 500050
#define INF (1LL<<60)
#define mid ((l+r)>>1)
#define ls l,mid,t<<1
#define rs mid+1,r,t<<1^1
using namespace std;
typedef long long LL;
inline int rd() { LL r; scanf("%lld",&r); return r; }
int n,m,ll,rr;
LL x,a[N],b[N],ans_a,ans_b;
struct Tag{
LL p,c,mp,mc;
Tag operator +=(Tag tmp) {
Tag ret;
ret.p = max(tmp.p+p,-INF);
ret.c = max(c+tmp.p , tmp.c);
// r.ex = 1;
ret.mp = max(p+tmp.mp , mp);
ret.mc = max(mc,max(tmp.mc , c+tmp.mp));
p = ret.p , c = ret.c , mc = ret.mc , mp = ret.mp;
}
}ag[4*N],now;
const Tag id = (Tag){0,-INF,0,-INF};
void push_down(int t) {
ag[t<<1] += ag[t];
ag[t<<1^1] += ag[t];
ag[t] = id;
}
void build(int l,int r,int t) {
if (l == r)
ag[t] = (Tag){a[l],-INF,a[l],-INF};
else {
build(ls); build(rs);
ag[t] = (Tag){ 0,-INF, 0,-INF};
}
}
void update(int l,int r,int t) {
if (l > rr || r < ll) return ;
if (l >= ll && r <= rr) { ag[t] += now; return ; }
push_down(t); update(ls); update(rs);
}
void query(int l,int r,int t) {
if (l > x || r < x) return ;
if (l == r) {
ans_a = max(ag[t].c , ag[t].p);
ans_b = max(ag[t].mc, ag[t].mp);
return ;
}
push_down(t); query(ls); query(rs);
}
int main() {
#ifndef ONLINE_JUDGE
// freopen("1.in","r",stdin);
// freopen("1.out","w",stdout);
#endif
n = rd(); m = rd();
for (int i=1;i<=n;i++) a[i] = b[i] = rd();
build(1,n,1);
for (int _=1;_<=m;_++) {
int cmd = rd();
if (cmd <= 3) {
ll = rd(); rr = rd(); x = rd();
if (cmd == 1) now = (Tag){x,-INF,x,-INF};
if (cmd == 2) now = (Tag){-x,0,-x,0};
if (cmd == 3) now = (Tag){-INF,x,-INF,x};
update(1,n,1);
} else {
x = rd();
ans_a = ans_b = 0LL;
query(1,n,1);
if (cmd == 4)
printf("%lld\n",ans_a);
else
printf("%lld\n",ans_b);
}
}
return 0;
}