很水的紫题
题目大意
给您一个长度为 (n) 的序列,您需要写一个数据结构,支持以下操作
- 查询 ([l,r]) 的最大字段和;
- 把 (a_p) 变成 (s) 。
solution
显然考虑线段树。
对于操作二,更改 (a_p) 在线段树中会影响到的结点即可。
单独考虑操作一。
其实有一种 (mathcal{O}(n)) 的求最大子段和的做法,但本题需要一个 (mathcal{O}(logn)) 的做法。
放个图:
对于这样一个区间,她的最大子段和只有三种可能:完全在左区间,完全在右区间,或者是贯穿了左右区间。
然后分别在左、右区间的最大字段和怎么维护待会讲
贯穿了中线的那个其实也很好维护,只要记录左区间的后缀,右区间的前缀,然后贯穿中线的就是这两个加起来。
所以这一整个区间的最大子段和就是上述三种可能的最大值。
那么现在问题聚焦在了如何求左区间的最大子段和、右区间的最大子段和。
以左区间为例,把左区间再次一分为二,现在要求左区间的最大子段和:
这种情况也就是左区间的最大子段和
这种情况也就是左区间和+右区间左半边最大子段和
然后不断递归就能搞到答案了。
右区间同理。
说到底,仍然是递归的思想。
有一些细节,见代码:
#include <iostream>
#include <stdio.h>
#include <math.h>
#define DEBUG printf("This is OK
")
using namespace std;
int n,q,a[5000100];
struct SegmentTree
{
int l,r;
long long lmax,rmax,ans,sum;
#define l(x) t[x].l
#define r(x) t[x].r
#define lmax(x) t[x].lmax //lmax是左区间最大子段和
#define rmax(x) t[x].rmax //rmax是右区间最大子段和
#define ans(x) t[x].ans //ans是整个 [l,r] 的最大子段和
#define sum(x) t[x].sum
};
SegmentTree t[4*5000010];
void merge(int p) //合并信息
{
sum(p)=sum(p*2)+sum(p*2+1); //区间和
lmax(p)=max(lmax(p*2),lmax(p*2+1)+sum(p*2));
rmax(p)=max(rmax(p*2+1),rmax(p*2)+sum(p*2+1));
ans(p)=max(max(ans(p*2),ans(p*2+1)),lmax(p*2+1)+rmax(p*2));
//以上合并信息同思路
}
void build(int p,int l,int r)
{
l(p)=l,r(p)=r;
if(l==r) {sum(p)=lmax(p)=rmax(p)=ans(p)=a[l];return ;}
int mid=(l+r)>>1;
build(p*2,l,mid);
build(p*2+1,mid+1,r);
merge(p); //合并信息
}
void change(int p,int x,int v)
{
if(l(p)==r(p))
{
sum(p)=lmax(p)=rmax(p)=ans(p)=v;
return ;
}
int mid=(l(p)+r(p))>>1;
if(x<=mid) change(p*2,x,v);
else change(p*2+1,x,v);
merge(p); //进行了更改,更新信息
}
SegmentTree ask(int p,int l,int r) //由于这里需要涉及多个结构体信息,所以直接把ask函数变成一个结构体函数
{
if(l<=l(p)&&r>=r(p)) return t[p];
int mid=(l(p)+r(p))>>1;
if(r<=mid) return ask(p*2,l,r); //如果右端点都在左半段,那直接去查左半段就好了
else if (l>mid) return ask(p*2+1,l,r); //同上
else //既有右端点,又有左端点
{
SegmentTree x=ask(p*2,l,r),y=ask(p*2+1,l,r),node;
//x,y是左、右区间的信息
node.sum=x.sum+y.sum;
node.lmax=max(x.lmax,x.sum+y.lmax);
node.rmax=max(y.rmax,y.sum+x.rmax);
node.ans=max(max(x.ans,y.ans),x.rmax+y.lmax); //和那个meger函数一样的思路。
return node;
}
}
inline int read()
{
int s=0,w=1; char ch=getchar();
while(ch<'0'||ch>'9') {if(ch=='-') w=-1;ch=getchar();}
while(ch>='0'&&ch<='9') s=s*10+ch-'0' , ch=getchar();
return s*w;
}
int main()
{
n=read(),q=read();
for(int i=1;i<=n;i++) a[i]=read();
build(1,1,n);
//DEBUG;
while(q--)
{
int K;
K=read();
if(K==1)
{
int l,r;
l=read(),r=read();
if(l>r) swap(l,r);
SegmentTree ans=ask(1,l,r);
cout<<ans.ans<<"
";
}
else
{
int p,s;
p=read(),s=read();
change(1,p,s);
}
}
return 0;
}