洛谷 SP1716 GSS3 - Can you answer these queries III
话说真的和GSS1有什么区别,除了一个修改操作...
0x00 思路
最大子段和是区间内一段连续元素的和,不妨当成一段区间来看,那么对于一段区间\([l,r]\)的最大子段和,有存在三种情况
定义\(mid = (l + r) / 2\)
-
完全位于\([l,mid]\)中
-
完全位于\([mid+1,r]\)中
-
左端点在\([l,mid]\),右端点在\([mid+1,r]\)中
这样,维护区间最大子段和,我们就要知道以下三个值:
-
子区间最大子段和
-
左区间的最大后缀和
-
右区间的最大前缀和
对于(1)我们很好理解,那么(2)(3)呢?
当左右端点分居在两个区间内时,我们发现它们之间没有关系,就是由一段前缀,一段后缀连接而成,分开讨论即可
接下来,为了维护最大前/后缀和,我们又要知道以下六个值
-
左区间的区间和/最大前缀和/最大后缀和
-
右区间的区间和/最大前缀和/最大后缀和
最大前缀和 = max(左区间最大前缀和 , 左区间区间和 + 右区间最大前缀和)
最大后缀和 = max(右区间最大后缀和 , 右区间区间和 + 左区间最大后缀和)
这下解决了,可以上线段树维护了
你问我区间和怎么维护?我只能呵呵
0x01 Code
#include<bits/stdc++.h>
using namespace std;
#define mid ((l+r)>>1)
#define ls (nod<<1)
#define rs (nod<<1|1)
#define lson ls,l,mid
#define rson rs,mid+1,r
int read(){
int x=0; char c=getchar(); int flag=1;
while(!isdigit(c)) { if(c=='-') flag=-1; c=getchar(); }
while(isdigit(c)) { x=((x+(x<<2))<<1)+(c^48); c=getchar(); }
return x*flag;
}
const int N=5e5+50;
int n,q;
struct Node{
int l,r;
int lv,rv,v,sum;
}t[N<<2];
Node Union(Node a,Node b){
Node c;
c.l=a.l; c.r=b.r;
c.lv=max(a.lv,a.sum+b.lv);
c.rv=max(b.rv,b.sum+a.rv);
c.sum=a.sum+b.sum;
c.v=max(max(a.v,b.v),a.rv+b.lv);
return c;
}
void pushup(int nod){
if(t[nod].l==t[nod].r) return ;
t[nod]=Union(t[ls],t[rs]);
}
void build(int nod,int l,int r){
t[nod].l=l; t[nod].r=r;
if(l==r){
t[nod].sum=t[nod].lv=t[nod].rv=t[nod].v=read();
return ;
}
build(lson); build(rson);
pushup(nod);
}
void update(int nod,int l,int r,int ps,int val){
if(l==r){
t[nod].lv=t[nod].rv=t[nod].v=t[nod].sum=val;
return ;
}
if(ps<=mid) update(lson,ps,val);
else update(rson,ps,val);
pushup(nod);
}
Node query(int nod,int l,int r,int ll,int rr){
if(ll==l&&r==rr){
return t[nod];
}
if(rr<=mid) return query(lson,ll,rr);
else if(ll>mid) return query(rson,ll,rr);
else return Union(query(lson,ll,mid),query(rson,mid+1,rr));
}
signed main(){
n=read();
build(1,1,n);
q=read();
while(q--){
int opt=read(),x=read(),y=read();
if(opt==0){
update(1,1,n,x,y);
}else{
printf("%d\n",query(1,1,n,x,y).v);
}
}
return 0;
}