(线段树 单点修改 最大连续字段和)
维护 除区间和 左端点 右端点 外 lmax 表示以区间左端为起点的最大连续和 rmax 表示以区间右端点为起点的最大连续和。
可以知道 L 到 R 的最大连续子段和要么是子区间的最大连续和 要么是相邻的两个区间 前一个的rmax和 后一个区间的lmax 连接起来。
所以在 build 和 change 中修改即可。
但 在查询答案时 不仅要传递各个区间的最大连续子段和,lmax和rmax 也要记录和传递,因为最优的答案不一定是各个分隔开的区间的各个最大值的最大值。一些区间可以合并即 前一个的rmax和 后一个区间的lmax 连接起来。
#include<bits/stdc++.h>
using namespace std;
const int MAXN=500050*2;
struct node{
int l,r,maxx,w,lmax,rmax;
}t[MAXN*4+10];
int a[MAXN];
node J={0,0,-MAXN,-MAXN,-MAXN,-MAXN};
void build(int l,int r,int k)
{
t[k].l=l; t[k].r=r;
if(l==r)
{
t[k].w=a[l];
t[k].lmax=t[k].w;
t[k].rmax=t[k].w;
t[k].maxx=t[k].w;
return;
}
int mid=(l+r)/2;
build(l,mid,k*2);
build(mid+1,r,k*2+1);
t[k].w=t[k*2].w+t[k*2+1].w;
t[k].maxx=max(t[k*2].maxx,max(t[k*2+1].maxx,t[k*2].rmax+t[k*2+1].lmax));
t[k].lmax=max(t[k*2].lmax,t[k*2].w+t[k*2+1].lmax);
t[k].rmax=max(t[k*2+1].rmax,t[k*2+1].w+t[k*2].rmax);
}
node get(int p,int l,int r)
{
if(l>t[p].r || r<t[p].l) return J;
if(l<=t[p].l && r>=t[p].r) return t[p];
//int mid=(t[p].l+t[p].r)/2;
node L,R,ans;
L=get(p*2,l,r);// 记录左子树
R=get(p*2+1,l,r);//记录右子树
ans.maxx=max(L.maxx,max(R.maxx,L.rmax+R.lmax));
ans.lmax=max(L.lmax,L.w+R.lmax);
ans.rmax=max(R.rmax,R.w+L.rmax);
return ans;
}
void change(int k,int x,int v)
{
if(t[k].l==t[k].r)
{
t[k].w=v;
t[k].lmax=t[k].w;
t[k].rmax=t[k].w;
t[k].maxx=t[k].w;
return;
}
int mid=(t[k].l+t[k].r)/2;
if(x<=mid) change(k*2,x,v);
else change(k*2+1,x,v);
t[k].maxx=max(t[k*2].maxx,max(t[k*2+1].maxx,t[k*2].rmax+t[k*2+1].lmax));
t[k].lmax=max(t[k*2].lmax,t[k*2].w+t[k*2+1].lmax);
t[k].rmax=max(t[k*2+1].rmax,t[k*2+1].w+t[k*2].rmax);
t[k].w=t[k*2].w+t[k*2+1].w;
}
int main()
{
freopen("b.in","r",stdin);
freopen("b.out","w",stdout);
int T,n;
cin>>n>>T;
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
build(1,n,1);
int opt,k1,k2;
for(int i=1;i<=T;i++)
{
scanf("%d%d%d",&opt,&k1,&k2);
if(opt==2) change(1,k1,k2);
if(opt==1)
{
if(k1>k2){int tmp=k1; k1=k2; k2=tmp;}
printf("%d\n",get(1,k1,k2).maxx);
}
}
return 0;
}