【SPOJ GSS3】Can you answer these queries III(动态区间最大段和)——杨子曰题目
传送门:线段树集合
题目描述
You are given a sequence A of N (N <= 50000) integers between -10000 and 10000. On this sequence you have to apply M (M <= 50000) operations:
modify the i-th element in the sequence or for given x y print max{Ai + Ai+1 + … + Aj | x<=i<=j<=y }.
输入格式:
The first line of input contains an integer N. The following line contains N integers, representing the sequence A1…AN.
The third line contains an integer M. The next M lines contain the operations in following form:
0 x y: modify Ax into y (|y|<=10000).
1 x y: print max{Ai + Ai+1 + … + Aj | x<=i<=j<=y }.
输出格式:
For each query, print an integer as the problem required.
输入样例:
4
1 2 3 4
4
1 1 3
0 3 -3
1 2 4
1 3 3
输出样例:
6
4
-3
给一个长度为n(n<=50000)的序列,再给出m个接着是m(m<=20000)个操作对于每个操作k,x,y,如果k=0那么第x个元素变为y,如果k=1就求出区间[x,y]里的子序列最大和
额……线段树我又来了
这次我们要维护的东西就有点多了,首先一个正常的人都能知道区间里的区间子序列最大和——ms肯定要维护(这不就是答案吗),现在我们要考虑想要算出ms,也就是pushup的时候要用到什么东西
我们来分类讨论一下:
这个区间的子序列最大和都在左区间:
嗯……不错,直接用ms[nod*2]更新上来
这个区间的子序列最大和都在右区间:
也直接用ms[nod*2+1]更新上来
但是最令人恼火的就是一半在左,一半在右,Look at the 图:
我们很明显地知道,蓝色和绿色部分很可能不是左右区间的子序列最大和,那到底是神马呢?
我觉得蓝色部分是左区间包括右端点的区间最大子序列和,同理绿色部分是右区间包括左端点的区间最大子序列和(←仔细阅读)
也就是我们还要维护每个区间从左边和右边开始的区间子序列最大和ls和rs
那我们就可以得到:
ms[nod]=max(ms[nos*2],ms[nod*2+1],rs[nod*2]+ls[nod*2+1]);
不要高兴太早,我们还要考虑ls和rs怎么pushup,我们以ls为例分类讨论一下:
这个区间的ls就是左区间的ls
二话不说,直接用ls[nod*2]更新上来
还有一种恶心的情况,叫做横跨了左右区间,look at the图:
我们会发现蓝色部分是左区间的和,而绿色部分是右区间的ls
这也就是说——我!们!还!要!维!护!一!个!sum
于是我们得出了这样的东西:
ls[nod]=max(ls[nod*2],sum[nod*2]+ls[nod*2+1]);
rs[nod]=max(rs[nod*2+1],sum[nod*2+1]+rs[nod*2]);
总结一波:到目前为止我们已经把我们要维护的东西维护完了——ms,ls,rs,sum
于是乎,我们得到了以下pushup:
void pushup(int nod){
ms[nod]=max(max(ms[nod*2],ms[nod*2+1]),rs[nod*2]+ls[nod*2+1]);
ls[nod]=max(ls[nod*2],sum[nod*2]+ls[nod*2+1]);
rs[nod]=max(rs[nod*2+1],sum[nod*2+1]+rs[nod*2]);
sum[nod]=sum[nod*2]+sum[nod*2+1];
}
这样一来build和update就变得非常常规:
void build(int l,int r,int nod){
if (l==r){
sum[nod]=ms[nod]=ls[nod]=rs[nod]=a[l];
return;
}
int mid=(l+r)/2;
build(l,mid,nod*2);
build(mid+1,r,nod*2+1);
pushup(nod);
}
void update(int l,int r,int k,int v,int nod){
if (l==r){
sum[nod]=ms[nod]=ls[nod]=rs[nod]=v;
return;
}
int mid=(l+r)/2;
if (k<=mid) update(l,mid,k,v,nod*2);
else update(mid+1,r,k,v,nod*2+1);
pushup(nod);
}
接下来,我们看——query
首先,如果查询区间在当前区间的左边,那我们就去左边找,如果查询区间在当前区间的右边,那我们就去右边找——这是地球人都知道的事情
我们现在要考虑的就是一般在左,一半在右的情况——其实与我们的pushup极像
我们要query的是ms,我们分三种情况讨论——把左边的ms给query出来,把右边的ms给query出来,然后我们还要把左边的rs给query,右边的ls给搞出来加在一起,三个东西取一个max
于是乎,我们又要写两个查ls和rs的query,比如查ls的query,当遇到一半在左一半在右的情况时,我们要把左边的ls给query出来,或者是query左边的sum 和右边的ls加一下,取个max
嗯,和pushup一样,很好理解
BBUUTT,你有没有注意到一个恐怖的事情,我!们!要!写!四!个!query!!!,分别求ms,ls,rs,sum
别急,这里有一个小技巧,我们可以把四个东西在一个query里求出——做法有两种
- 把四个东西放在一个结构体里,return一个结构体
- 把四个东西放进实参里,然后传出
我这里选择了第2种:
void query(int l,int r,int ll,int rr,int nod,int &s,int &ans,int &ans_l,int &ans_r){
if (l==ll && r==rr){
ans=ms[nod];
ans_l=ls[nod];
ans_r=rs[nod];
s=sum[nod];
return;
}
int mid=(l+r)/2;
if (rr<=mid) query(l,mid,ll,rr,nod*2,s,ans,ans_l,ans_r);
else if (ll>mid) query(mid+1,r,ll,rr,nod*2+1,s,ans,ans_l,ans_r);
else{
int l_ans,r_ans,l_ans_l,l_ans_r,r_ans_l,r_ans_r,l_s,r_s;
query(l,mid,ll,mid,nod*2,l_s,l_ans,l_ans_l,l_ans_r);
query(mid+1,r,mid+1,rr,nod*2+1,r_s,r_ans,r_ans_l,r_ans_r);
ans=max(max(l_ans,r_ans),l_ans_r+r_ans_l);
ans_l=max(l_ans_l,l_s+r_ans_l);
ans_r=max(r_ans_r,r_s+l_ans_r);
s=l_s+r_s;
}
}
相信你看变量名看得已经头晕了,简单解释一下:
- s:我们现在要求的区间的和(实参)
- ans:我们现在要求的ms(实参)
- ans_l:我们现在要求的ls(实参)
- ans_r:我们现在要求的rs(实参)
- l_XXX:左区间的XXX
- r_XXX:右区间的XXX
OK,完事
搞定了这道题以后你可以去试试这道题:【SPOJ2916 GSS5】Can you answer these queries V
c++代码:
#include<cstdio>
#include<iostream>
using namespace std;
const int maxn=50005;
int a[maxn],sum[maxn*4],ms[maxn*4],ls[maxn*4],rs[maxn*4];
int max(int x,int y){
return x>y?x:y;
}
void pushup(int nod){
ms[nod]=max(max(ms[nod*2],ms[nod*2+1]),rs[nod*2]+ls[nod*2+1]);
ls[nod]=max(ls[nod*2],sum[nod*2]+ls[nod*2+1]);
rs[nod]=max(rs[nod*2+1],sum[nod*2+1]+rs[nod*2]);
sum[nod]=sum[nod*2]+sum[nod*2+1];
}
void build(int l,int r,int nod){
if (l==r){
sum[nod]=ms[nod]=ls[nod]=rs[nod]=a[l];
return;
}
int mid=(l+r)/2;
build(l,mid,nod*2);
build(mid+1,r,nod*2+1);
pushup(nod);
}
void update(int l,int r,int k,int v,int nod){
if (l==r){
sum[nod]=ms[nod]=ls[nod]=rs[nod]=v;
return;
}
int mid=(l+r)/2;
if (k<=mid) update(l,mid,k,v,nod*2);
else update(mid+1,r,k,v,nod*2+1);
pushup(nod);
}
void query(int l,int r,int ll,int rr,int nod,int &s,int &ans,int &ans_l,int &ans_r){
if (l==ll && r==rr){
ans=ms[nod];
ans_l=ls[nod];
ans_r=rs[nod];
s=sum[nod];
return;
}
int mid=(l+r)/2;
if (rr<=mid) query(l,mid,ll,rr,nod*2,s,ans,ans_l,ans_r);
else if (ll>mid) query(mid+1,r,ll,rr,nod*2+1,s,ans,ans_l,ans_r);
else{
int l_ans,r_ans,l_ans_l,l_ans_r,r_ans_l,r_ans_r,l_s,r_s;
query(l,mid,ll,mid,nod*2,l_s,l_ans,l_ans_l,l_ans_r);
query(mid+1,r,mid+1,rr,nod*2+1,r_s,r_ans,r_ans_l,r_ans_r);
ans=max(max(l_ans,r_ans),l_ans_r+r_ans_l);
ans_l=max(l_ans_l,l_s+r_ans_l);
ans_r=max(r_ans_r,r_s+l_ans_r);
s=l_s+r_s;
}
}
int main(){
int n;
scanf("%d",&n);
for (int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
build(1,n,1);
int m;
scanf("%d",&m);
while(m--){
int k,x,y,ans=0,ansl=0,ansr=0,s=0;
scanf("%d%d%d",&k,&x,&y);
if (k==0) update(1,n,x,y,1);
else{
query(1,n,x,y,1,s,ans,ansl,ansr);
printf("%d\n",ans);
}
}
return 0;
}
于XJ机房607