题目链接:
https://vjudge.net/problem/UVALive-3938
题目解析:
构造线段树,维护最大区间和,最大前缀和,最大后缀和,通过分治的思想进行区间合并。
AC代码:
#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;
}