线段树维护最大子段和
线段树应用
线段树维护最大子段和
link
GSS3 - Can you answer these queries III
题面翻译
\(n\) 个数,\(q\) 次操作
操作0 x y
把\(A_x\) 修改为\(y\)
操作1 l r
询问区间\([l, r]\) 的最大子段和
样例 #1
样例输入 #1
4
1 2 3 4
4
1 1 3
0 3 -3
1 2 4
1 3 3
样例输出 #1
6
4
-3
\(solution\)
我们考虑对于每一个区间进行分治,把区间分成左右两部分,然后分别计算出左右两部分的值。
现在我们考虑怎么合并答案。
合并答案
有这么几种情况:
黑色长方形表示原序列,蓝色部分表示整个序列的最大子段和,
这种情况时,\(\textbf{最大子段和就是右边的最大子段和}\)。
这种情况时,\(\textbf{最大子段和就是左边的最大子段和}\)。
这种情况时,最大子段和跨越了左右两个区间,
\(\textbf{此时的最大子段和等于左边区间的最大后缀和加上右边区间的最大前缀和}\)。
我们可以用线段树来维护。
\(sum\)表示区间和,\(pre\) 表示最大前缀和,\(suf\) 表示最大后缀和,\(res\) 表示最大子段和。
然后我们分别考虑对于各个量的维护。
各个量的维护
1. 区间和
这个没什么好说的,左右直接加。
2. 最大子段和
这个前面合并答案的时候已经说了。
3.最大前缀和 最大后缀和
因为这两个是一样的,所以我们以最大前缀和为例,分析一下合并时的维护。
有这么几种情况:
黑色长方形表示原序列,蓝色部分表示整个序列的最大前缀和,
这种情况时,\(\textbf{最大前缀和为左边的最大前缀和}\)。
这种情况时,\(\textbf{最大前缀和为左边区间的和加上右边区间的最大前缀和}\)。
最大后缀和也是同理。
\(code\)
#include<iostream>
#define lc now<<1
#define rc now<<1|1
#define int long long
using namespace std;
const int N=1e5+5;
struct node
{
int pre,suf,sum,res;
};
node tree[N<<2];
int n,q;
void push_up(int now)
{
tree[now].res=max(tree[lc].res,tree[rc].res);
tree[now].res=max(tree[now].res,tree[lc].suf+tree[rc].pre);
tree[now].sum=tree[lc].sum+tree[rc].sum;
tree[now].pre=max(tree[lc].pre,tree[lc].sum+tree[rc].pre);
tree[now].suf=max(tree[rc].suf,tree[rc].sum+tree[lc].suf);
}
void build(int now,int l,int r)
{
if(l==r)
{
scanf("%lld",&tree[now].sum);
tree[now].pre=tree[now].suf=tree[now].res=tree[now].sum;
return ;
}
int mid=(l+r)>>1;
build(lc,l,mid);
build(rc,mid+1,r);
push_up(now);
}
void update(int now,int l,int r,int pos,int v)
{
if(l==r)
{
tree[now].sum=tree[now].res=tree[now].suf=tree[now].pre=v;
return ;
}
int mid=(l+r)>>1;
if(pos<=mid) update(lc,l,mid,pos,v);
else update(rc,mid+1,r,pos,v);
push_up(now);
}
node merga(node La,node Ra)
{
node ress;
ress.sum=La.sum+Ra.sum;
ress.res=max(La.res,Ra.res);
ress.res=max(ress.res,La.suf+Ra.pre);
ress.pre=max(La.pre,La.sum+Ra.pre);
ress.suf=max(Ra.suf,Ra.sum+La.suf);
return ress;
}
node query(int now,int l,int r,int L,int R)
{
if(l>=L&&r<=R)
return tree[now];
int mid=(l+r)>>1;
node ans;
if(R<=mid) return query(lc,l,mid,L,R);
if(L>mid) return query(rc,mid+1,r,L,R);
ans=merga(query(lc,l,mid,L,R),query(rc,mid+1,r,L,R));
return ans;
}
signed main()
{
scanf("%lld",&n);
build(1,1,n);
scanf("%lld",&q);
for(int i=1;i<=q;++i)
{
int op,x,y,l,r;
scanf("%lld",&op);
if(op==0)
{
scanf("%lld %lld",&x,&y);
update(1,1,n,x,y);
}
if(op==1)
{
scanf("%lld %lld",&l,&r);
node Ans=query(1,1,n,l,r);
printf("%lld\n",Ans.res);
}
}
return 0;
}