CH4301 Can you answer on these queries III
维护一个序列,支持单点修改和查询区间\([x,y]\)中的最大连续子段和
问题在于如何维护最大连续字段和,或者说怎么存下最大连续子段和能让它满足区间加法,这也是用线段树维护某个东西时要考虑的核心问题
考虑最大连续字段和的形成方式,可能是整个区间\([l,r]\)的和,可能是紧靠\(l\)这边的一段连续和,可能是紧靠\(r\)这边的一段连续和
所以我们可以在线段树中额外维护区间和\(sum\),左端最大连续子段\(lmax\)和和右端连续子段和\(rmax\),以及最大连续字段和\(dat\)
只需要在从下往上更新的地方注意这几个量的关系,进行维护即可求解,转移的时候有点像区间\(dp\)
特别注意:此时如果\(x>y\),请交换\(x,y\)
#include<bits/stdc++.h>
using namespace std;
const int N=500010;
int n,m,a[N];
struct SegmentTree {
int l,r,dat,lmax,rmax,sum;
} t[N<<2];
void build(int p,int l,int r) {
t[p].l=l;
t[p].r=r;
if(l==r) {
t[p].lmax=t[p].rmax=t[p].dat=t[p].sum=a[l];
return;
}
int mid=(l+r)>>1;
build(p<<1,l,mid),build(p<<1|1,mid+1,r);
t[p].dat=max(t[p<<1].rmax+t[p<<1|1].lmax,max(t[p<<1].dat,t[p<<1|1].dat));
t[p].sum=t[p<<1].sum+t[p<<1|1].sum;
t[p].lmax=max(t[p<<1].lmax,t[p<<1].sum+t[p<<1|1].lmax);
t[p].rmax=max(t[p<<1|1].rmax,t[p<<1].rmax+t[p<<1|1].sum);
return;
}
void update(int p,int x,int v) {
if(t[p].l==t[p].r) {
t[p].lmax=t[p].rmax=t[p].dat=t[p].sum=v;
return;
}
int mid=(t[p].l+t[p].r)>>1;
if(x<=mid) update(p<<1,x,v);
else update(p<<1|1,x,v);
t[p].dat=max(t[p<<1].rmax+t[p<<1|1].lmax,max(t[p<<1].dat,t[p<<1|1].dat));
t[p].sum=t[p<<1].sum+t[p<<1|1].sum;
t[p].lmax=max(t[p<<1].lmax,t[p<<1].sum+t[p<<1|1].lmax);
t[p].rmax=max(t[p<<1|1].rmax,t[p<<1].rmax+t[p<<1|1].sum);
}
SegmentTree query(int p,int L,int R) {
SegmentTree ret,tmpl,tmpr;
if(L<=t[p].l&&t[p].r<=R) return t[p];
int mid=(t[p].l+t[p].r)>>1;
if(R<=mid) return query(p<<1,L,R);
if(mid<L) return query(p<<1|1,L,R);
tmpl=query(p<<1,L,R);
tmpr=query(p<<1|1,L,R);
ret.dat=max(tmpl.rmax+tmpr.lmax,max(tmpl.dat,tmpr.dat));
ret.lmax=max(tmpl.lmax,tmpl.sum+tmpr.lmax);
ret.rmax=max(tmpr.rmax,tmpr.sum+tmpl.rmax);
ret.sum=tmpl.sum+tmpr.sum;
return ret;
}
int main() {
scanf("%d%d",&n,&m);
for(int i=1; i<=n; i++) scanf("%d",&a[i]);
build(1,1,n);
while(m--) {
int k,x,y;
scanf("%d%d%d",&k,&x,&y);
if(k==1) {
if(x>y) swap(x,y);
printf("%d\n",query(1,x,y).dat);
} else update(1,x,y);
}
return 0;
}
$$Life \quad is \quad fantastic!$$