[模板] 动态修改最大子段和
建一棵线段树,一个节点维护$4$个东西,最大前缀和,最大后缀和,最大连续和,总的权值和,先说最大前缀和怎么进行维护,我们只需看一看左儿子的最大前缀和,但是有可能终点不止包含左节点的所有,还有可能有右节点的最长前缀和,所以就将左儿子的所有权值加上右儿子的最长前缀和,然后其他都是这样处理。在查询中有可能右端点在$mid$的右面,所以需要特判取最优解
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; inline int read() { int f=1,ans=0;char c; while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } struct node{ int ans,ls,rs,sum; }xx[2000001]; int n,m,val[2000001]; void pushdown(int x){ xx[x].sum=xx[x<<1].sum+xx[x<<1|1].sum; xx[x].ls=max(xx[x<<1].ls,xx[x<<1].sum+xx[x<<1|1].ls); xx[x].rs=max(xx[x<<1|1].rs,xx[x<<1|1].sum+xx[x<<1].rs); xx[x].ans=max(xx[x<<1].ans,max(xx[x<<1|1].ans,xx[x<<1].rs+xx[x<<1|1].ls)); return; } void build(int k,int l,int r){ if(l==r){ xx[k].ls=xx[k].rs=xx[k].ans=xx[k].sum=val[l]; return; } int mid=l+r>>1; build(k<<1,l,mid),build(k<<1|1,mid+1,r); pushdown(k); return; } void change(int k,int l,int r,int x,int y,int w){ if(x<=l&&r<=y){ xx[k].ls=xx[k].rs=xx[k].ans=xx[k].sum=w; return; } int mid=l+r>>1; if(x<=mid) change(k<<1,l,mid,x,y,w); if(mid<y) change(k<<1|1,mid+1,r,x,y,w); pushdown(k); return; } node query(int k,int l,int r,int x,int y){ if(x<=l&&r<=y) { return xx[k]; } int mid=l+r>>1; if(x<=mid&&!(mid<y)) return query(k<<1,l,mid,x,y); else if(!(x<=mid)&&mid<y) return query(k<<1|1,mid+1,r,x,y); else{ node st,t1=query(k<<1,l,mid,x,y),t2=query(k<<1|1,mid+1,r,x,y); st.sum=t1.sum+t2.sum; st.ls=max(t1.ls,t1.sum+t2.ls); st.rs=max(t2.rs,t2.sum+t1.rs); st.ans=max(t1.ans,max(t2.ans,t1.rs+t2.ls)); return st; } } int main() { n=read(),m=read(); for(int i=1;i<=n;i++) val[i]=read(); build(1,1,n); while(m--){ int pd=read(); if(pd==2){ int x=read(),w=read(); change(1,1,n,x,x,w); } else if(pd==1){ int l=read(),r=read(); if(l>r) swap(l,r); printf("%d\n",query(1,1,n,l,r).ans); } } }