给定长度为N的数列A,以及M条指令,每条指令可能是以下两种之一:
1、“1 x y”,查询区间 [x,y] 中的最大连续子段和,即 maxx≤l≤r≤y{∑ri=lA[i]}。
2、“2 x y”,把 A[x] 改成 y。
对于每个查询指令,输出一个整数表示答案。
输入格式
第一行两个整数N,M。
第二行N个整数A[i]。
接下来M行每行3个整数k,x,y,k=1表示查询(此时如果x>y,请交换x,y),k=2表示修改。
输出格式
对于每个查询指令输出一个整数表示答案。
每个答案占一行。
数据范围
N≤500000,M≤100000
输入样例:
5 3
1 2 -3 4 5
1 2 3
2 2 -1
1 3 2
输出样例:
2
-1
解析:
我们需要维护4个信息
1.最大前缀和
2.最大后缀和
3.最大连续子段和
4.区间和
4的操作很好维护。
我们来讲3
如果询问区间正好在左区间那么很简单就是左区间的最大子段和
如果询问区间正好在右区间那么很简单就是右区间的最大字段和
如果询问区间在左右子区间,那么就是左区间的最大后缀和+右区间的最大前缀和
三个取max.
讲1和讲2
如果询问区间正好在左区间很简单就是左区间的最大前缀和
如果询问区间正好在右区间很简单就是右区间的最大后缀和。
如果询问区间覆盖了左区间并且也存在右区间中,那么就是左区间的区间和+右区间的最大前缀和
如果询问区间覆盖了右区间并且也存在左区间中,那么就是右区间的区间和+左区间的最大前缀和
四者取max
#include<bits/stdc++.h>
using namespace std;
const int N=5e5+1000;
int n,m,x,y;
int a[N];
struct Node
{
int l,r; //区间左右端点
int sum;//区间和
int lmax;//最大前缀和
int rmax;//最大后缀和
int tmax;//最大连续子段和
}tr[N<<4];
void push_up(Node &u,Node &l,Node &r)
{
u.sum=l.sum+r.sum;
u.lmax=max(l.lmax,l.sum+r.lmax);
u.rmax=max(r.rmax,r.sum+l.rmax);
u.tmax=max(max(l.tmax,r.tmax),l.rmax+r.lmax);
}
void push_up(int u)
{
push_up(tr[u],tr[u<<1],tr[u<<1|1]);
}
void build(int u,int l,int r)
{
if(l==r)
{
tr[u]={l,r,a[r],a[r],a[r],a[r]};
return ;
}
int mid=l+r>>1;
tr[u]={l,r};
build(u<<1,l,mid);
build(u<<1|1,mid+1,r);
push_up(u);
}
void update(int u,int x,int v)
{
if(tr[u].l==tr[u].r)
{
tr[u]={x,x,v,v,v,v};
return ;
}
int mid=tr[u].l+tr[u].r>>1;
if(x<=mid) update(u<<1,x,v);
else update(u<<1|1,x,v);
push_up(u);
}
Node query(int u,int l,int r)
{
if(l<=tr[u].l&&r>=tr[u].r) return tr[u];
int mid=tr[u].l+tr[u].r>>1;
if(r<=mid) return query(u<<1,l,r);//在左区间
else if(l>mid) return query(u<<1|1,l,r);//在右区间
else
{
auto left=query(u<<1,l,r);
auto right=query(u<<1|1,l,r);
Node res;
push_up(res,left,right) ;
return res;
}
}
int main()
{
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
build(1,1,n);
int op;
while(m--)
{
scanf("%d %d %d",&op,&x,&y);
if(op==1)
{
if(x>y) swap(x,y) ;
cout<<query(1,x,y).tmax<<endl;
}
else
{
update(1,x,y);
}
}
}