题意:给定了一个长度为n的数组,m次询问,给出l,r,找到该区间的最大连续和。
线段树维护一个最大的区间,细想当l==r的时候区间最大连续和就是[l,r],向上合并的时候。
画图可知,左子树的最大连续区间和,以及右子树的最大连续区间和,需要比较大小外,还有合并完,左子树的后缀与右子树的前缀需要判断一下大小以便更新区间连续最大值。所以在维护以最大的前缀和以及后缀和。
代码来源:https://blog.csdn.net/Q755100802/article/details/83758120
综合情况就是三种,两个区间合并,最大连续区间,左端点右端点都在左子树或者右子树,或者一左一右。
#include<cmath>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cstdlib>
#include<istream>
#include<vector>
#include<stack>
#include<set>
#include<map>
#include<algorithm>
#include<queue>
#define inf 0x3f3f3f3f
#define llinf 0x3f3f3f3f3f3f3f3f
#define MAX_len 200005*4
using namespace std;
typedef long long ll;
typedef pair<int,int> PP;
const int mod=1e9+7;
const int MAXlen=(5e5+10)*2;
double eps=1e-4;
int n,m;
ll a[500010];
struct A {
int l,r;
int pre,suf;
PP ans;
}tree[MAX_len];
ll cal(PP x) {
return a[x.second]-a[x.first-1];
}
PP cmp(PP x,PP y) {
ll t1=cal(x);
ll t2=cal(y);
if(t1!=t2) {
return t1>t2?x:y;
}
return x<y?x:y;
}
A comp(A x,A y) {
A res;
res.l=x.l;
res.r=y.r;
res.pre=cmp(PP(x.l,x.pre),PP(x.l,y.pre)).second;
res.suf=cmp(PP(y.suf,y.r),PP(x.suf,y.r)).first;
res.ans=cmp(PP(x.suf,y.pre),cmp(x.ans,y.ans));
return res;
}
void build_tree(int p,int l1,int r1)
{
if(l1==r1)
{
tree[p].l=tree[p].r=tree[p].pre=tree[p].suf=l1;
tree[p].ans=make_pair(l1,r1);
return ;
}
int mid=(l1+r1)>>1;
build_tree((p<<1),l1,mid);
build_tree((p*2+1),mid+1,r1);
tree[p]=comp(tree[p*2],tree[p*2+1]);
}
A query(int p,int l1,int r1)
{
if(l1<=tree[p].l&&r1>=tree[p].r)
{
return tree[p];
}
int mid=(tree[p].l+tree[p].r)/2;
A res;
if(l1<=mid&&r1>mid) {
res=comp(query(p*2,l1,r1),query(p*2+1,l1,r1));
}
else if(l1<=mid) {
res=query(p*2,l1,r1);
}
else {
res=query(p*2+1,l1,r1);
}
return res;
}
int main()
{
int test=1;
while(~scanf("%d %d",&n,&m)) {
int i,j;
a[0]=0;
for(i=1;i<=n;i++) {
scanf("%lld",&a[i]);
a[i]+=a[i-1];
}
build_tree(1,1,n);
printf("Case %d:\n",test++);
while(m--) {
int x,y;
scanf("%d %d",&x,&y);
PP temp=query(1,x,y).ans;
printf("%d %d\n",temp.first,temp.second);
}
}
return 0;
}