题目链接:https://vjudge.net/contest/238229#problem/E
思路:说实话,千万句题解思路不如几行关键代码,靠大神的代码才让我真正弄懂该怎么做T_T 详见:大佬博客
我觉得这题应该注意的地方:
1.线段树节点应该保存的东西:最大连续和的起点和终点,可以用pair保存;最大前缀和的终点prer;最大后缀和的起点sufl。
那么,我们在构建的时候,从小区间回归到大区间,要确定的就是最大连续和的起点和终点的位置,有三种情况:
(1)比较之后,起点和终点都在左边或右边,则大区间.sub=max(左小区间.sub,又小区间.sub)
(2)起点在左边,终点在右边,那么也就只有可能从左边的最大后缀和起点开始,到右边的最大前缀和终点结束,这一定是第三
种情况最大的合并连续和了。
以上这两点正是合并两个子区间的代码精髓所在,具体见代码。(注释写的很详细了,主要怕我自己看不懂)
2.保存一个数组前缀和,用于计算连续区间长度的大小。
还有许多,都在注释中了,这题对我这蒟蒻不友好啊......
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;
const int maxn=500000+10;
typedef long long ll;
typedef pair<int,int> par;
ll a[maxn];
int n,q,temp,l,r;
ll sum[maxn];//前缀和,要求一段区间的值就是sum[r]-sum[l-1]
struct node
{
int l,r,prer,sufl;//左右节点,最大前缀和终点,最大后缀和起点
par sub;//结果
}nodes[maxn<<2];
ll cal(par x)
{
return sum[x.second]-sum[x.first-1];//求区间和
}
//比较两个区间和大小
par cmp(par x,par y)
{
ll a=cal(x);
ll b=cal(y);
if(a!=b) return a>b?x:y;
return x<y?x:y;
}
//合并
node com(node x,node y)
{
node res;
res.l=x.l;
res.r=y.r;
res.prer=cmp(par(x.l,x.prer),par(x.l,y.prer)).second;//新节点的最大前缀和终点,画个图就能理解
res.sufl=cmp(par(y.sufl,y.r),par(x.sufl,y.r)).first;//新节点的最大后缀和
//答案的区间只会有三种情况,要么左边,要么右边,要么中间。
res.sub=cmp(par(x.sufl,y.prer),cmp(x.sub,y.sub));
return res;
}
void build(int l,int r,int root)
{
if(l==r)
{
//根节点
nodes[root].l=nodes[root].r=nodes[root].prer=nodes[root].sufl=l;
nodes[root].sub=par(l,l);
return;
}
int m=(l+r)/2;
build(l,m,root*2);//左节点
build(m+1,r,root*2+1);//右节点
nodes[root]=com(nodes[root*2],nodes[root*2+1]);//合并
}
node query(int l,int r,int root)
{
if(nodes[root].l>=l&&nodes[root].r<=r)return nodes[root];
int m=(nodes[root].l+nodes[root].r)/2;
node res;
if(l<=m&&r>m)res=com(query(l,r,root*2),query(l,r,root*2+1));//查询区间包含左右合集
else if (l<=m) res=query(l,r,root*2); //l<=m且r<=m,说明只在左边
else res=query(l,r,root*2+1); //l>m且r>m说明只在右边
return res;
}
int main()
{
int index=1;
while(scanf("%d%d",&n,&q)!=EOF)
{
memset(sum,0,sizeof(sum));
for(int i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
sum[i]=a[i]+sum[i-1];
}
build(1,n,1);//构建线段树
printf("Case %d:\n",index++);
for(int i=1;i<=q;++i)
{
scanf("%d%d",&l,&r);
par ans=query(l,r,1).sub;
printf("%d %d\n",ans.first,ans.second);
}
}
return 0;
}