洛谷4513 小白逛公园(线段树)
嗒嗒嗒嗒嗒嗒
【题目分析】
题意。。。很明显看出要维护区间子序列最大值,那么问题就在于如何在区间合并的时候更新区间子序列最大值。
可以推出,区间子序列最大值要么是从最左边开始的一段,要么是最右边向左的一段,要么是中间的一段,对于第一种,我们发现要么是左儿子最左边的一段,要么是左儿子+右儿子左边一段,第二种同理。
对于第三种,要么是左儿子的最大值,要么是右儿子的最大值,要么是左儿子的右边的一段与右儿子的左边的一段拼起来。
然后就over啦
【代码~】
#include<bits/stdc++.h>
using namespace std;
const int MAXN=5e5+10;
struct Tree{
int l,r;
int ls,rs,maxs;
int sum;
}tr[MAXN<<2];
int n,m;
int scor[MAXN];
void push_up(int root)
{
tr[root].sum=tr[root<<1].sum+tr[root<<1|1].sum;
tr[root].ls=max(tr[root<<1].ls,tr[root<<1].sum+tr[root<<1|1].ls);
tr[root].rs=max(tr[root<<1|1].rs,tr[root<<1|1].sum+tr[root<<1].rs);
tr[root].maxs=max(max(tr[root<<1].maxs,tr[root<<1|1].maxs),tr[root<<1].rs+tr[root<<1|1].ls);
}
void build(int root,int l,int r)
{
tr[root].l=l;
tr[root].r=r;
if(l==r)
{
tr[root].ls=tr[root].rs=tr[root].maxs=tr[root].sum=scor[l];
return ;
}
int mid=l+r>>1;
build(root<<1,l,mid);
build(root<<1|1,mid+1,r);
push_up(root);
}
void update(int root,int l,int r,int x,int key)
{
if(l==r)
{
tr[root].ls=tr[root].rs=tr[root].maxs=tr[root].sum=key;
return ;
}
int mid=l+r>>1;
if(x<=mid)
update(root<<1,l,mid,x,key);
else
update(root<<1|1,mid+1,r,x,key);
push_up(root);
}
Tree query(int root,int L,int R)
{
if(L<=tr[root].l&&tr[root].r<=R)
return tr[root];
int mid=tr[root].l+tr[root].r>>1;
if(R<=mid)
return query(root<<1,L,R);
if(L>mid)
return query(root<<1|1,L,R);
Tree ans,ans1=query(root<<1,L,mid),ans2=query(root<<1|1,mid+1,R);
ans.l=ans1.l,ans.r=ans2.r;
ans.ls=max(ans1.ls,ans1.sum+ans2.ls);
ans.rs=max(ans2.rs,ans2.sum+ans1.rs);
ans.maxs=max(max(ans1.maxs,ans2.maxs),ans1.rs+ans2.ls);
ans.sum=ans1.sum+ans2.sum;
return ans;
}
int Read()
{
int i=0,f=1;
char c;
for(c=getchar();(c>'9'||c<'0')&&c!='-';c=getchar());
if(c=='-')
f=-1,c=getchar();
for(;c>='0'&&c<='9';c=getchar())
i=(i<<3)+(i<<1)+c-'0';
return i*f;
}
int main()
{
n=Read(),m=Read();
for(int i=1;i<=n;++i)
scor[i]=Read();
build(1,1,n);
while(m--)
{
int cz=Read(),l=Read(),r=Read();
if(cz==1)
{
if(l>r)
swap(l,r);
printf("%d\n",query(1,l,r).maxs);
}
else
update(1,1,n,l,r);
}
return 0;
}