解题思路: 一开始表示线段树的结构体中只有l,r,要维护当前区间的最大子段和,那么向其中加入表示最大子段和的变量tmax,然后我们考虑如何从下往上去更新tmax,发现以我们现在的变量无法对其进行更新,那么来看一下当前区间的最大子段和等于什么,其应该等于左子树的最大子段和,右子树的最大子段和,还有一种可能是最大子段和横跨左右两个区间,这种情况下最大子段和就等于左区间的最大后缀和+右区间的最大前缀和,这样以来tmax可以得到更新了,但是又多出来2个变量,最大前缀和lmax,最大后缀和rmax,这两个变量怎么更新来?,当前区间的最大前缀和可能来源于2总情况,一种是左区间的最大前缀和,一种是左区间的和+右区间的最大前缀和,当前区间的最大后缀和同理=max(右区间的最大后缀和,右区间的和+左区间的最大后缀和),然后又出现了一个变量区间和。。。。,幸运的是这个属性比较好维护,所以综上所述,我们除了l,r之外还需要维护4个变量。
直接上代码:
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <cmath>
const int N=5e5+10;
using namespace std;
int a[N];
struct node{
int l,r;
int tmax;
int lmax,rmax;
int sum;
}tr[N<<2];
void pushup(node &u,node &l,node &r)
{
u.lmax=max(l.lmax,l.sum+r.lmax);
u.rmax=max(r.rmax,l.rmax+r.sum);
u.sum=l.sum+r.sum;
u.tmax=max(max(l.tmax,r.tmax),l.rmax+r.lmax);
}
void pushup(int u)
{
pushup(tr[u],tr[u<<1],tr[u<<1|1]);
}
void build(int rt,int l,int r)
{
if(l==r)
{
tr[rt]={l,l,a[l],a[l],a[l],a[l]};
return ;
}
else
{
tr[rt].l=l;
tr[rt].r=r;
int mid=l+r>>1;
build(rt<<1,l,mid);
build(rt<<1|1,mid+1,r);
pushup(rt);
}
}
void modify(int rt,int x,int y)
{
if(tr[rt].l==x&&tr[rt].r==x)
{
tr[rt]={x,x,y,y,y,y};
return ;
}
else
{
int mid=tr[rt].l+tr[rt].r>>1;
if(x<=mid)
modify(rt<<1,x,y);
if(x>mid)
modify(rt<<1|1,x,y);
pushup(rt);
}
}
node query(int rt,int l,int r)
{
if(tr[rt].l>=l&&tr[rt].r<=r)
return tr[rt];
else
{
int mid=tr[rt].l+tr[rt].r>>1;
if(r<=mid)
return query(rt<<1,l,r);
else if(l>mid)
return query(rt<<1|1,l,r);
else//如果要查询的区间横跨左右两个区间,那么将左右区间的答案算出后再合并成一个新的节点就好了
{
node left=query(rt<<1,l,r);
node right=query(rt<<1|1,l,r);
node res;
pushup(res,left,right);
return res;
}
}
}
int main()
{
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++)
cin>>a[i];
build(1,1,n);
for(int i=1;i<=m;i++)
{
int op,x,y;
cin>>op>>x>>y;
if(op==1)
{
if(x>y)
swap(x,y);
cout<<query(1,x,y).tmax<<endl;
}
else
modify(1,x,y);
}
return 0;
}