题意:对于询问(a,b),需要找到
两个下标x和y,使得a<=x<=y<=b,并且Dx+Dx+1+....+Dy尽量大。如果有多组满足条件的x和y,x
分析:应尽量小。如果还有多解y也应尽量小。
构造一颗线段树节点保存区间最值,最大前缀和,最大后缀和,及他们对应的下标。
对于某一区间最值的更新:
1. 左区间的最值
2 右区间的最值
3 左区间的最大后缀和 + 右区间的最大前缀和
对于最大前缀和的更新:
1 左区间的最大前缀和
2 左区间的和 + 右区间的最大前缀和
下标在更新最值,最大前缀,最大后缀 时 一同更新。
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define havemid int m=(l+r)>>1
#define left (rt<<1)
#define right (rt<<1|1)
#define LL long long
const int maxn=500100;
struct node {
LL lsum,rsum,sum,mov;// 最大前缀和,最大后缀和,区间和,最大值
int ll,rr,l,r;//最大前缀和端点, 最大后缀和端点,最大区间左右端点
}a[maxn<<2];
void pushup(int l,int r,int rt){
havemid;
a[rt].sum=a[left].sum+a[right].sum; //区间和=左右+
a[rt].lsum=a[left].lsum; //父前缀和=左儿子前缀和
a[rt].rsum=a[right].rsum; //右
a[rt].ll=a[left].ll; //父前缀的端点=左儿子前缀的端点
a[rt].rr=a[right].rr; //右
if(a[left].sum+a[right].lsum>a[rt].lsum){//左儿子的和+右儿子的前缀>区间前缀的话
a[rt].lsum=a[left].sum+a[right].lsum;//更新父前缀和and端点
a[rt].ll=a[right].ll;
}
if(a[right].sum+a[left].rsum>a[rt].rsum){//右同
a[rt].rsum=a[right].sum+a[left].rsum;
a[rt].rr=a[left].rr;
}
int tmp;
if(a[left].mov>=a[right].mov)tmp=left;
else tmp=right; //左右区间最值大的付给区间最值
a[rt].mov=a[tmp].mov;a[rt].l=a[tmp].l;a[rt].r=a[tmp].r;
LL tt=a[left].rsum+a[right].lsum; //左的后缀和+右的前缀和>区间最值 并且端点小 更新
if(tt>a[rt].mov||(tt==a[rt].mov&&a[left].rr<a[rt].l)||
(tt==a[rt].mov&&a[left].rr==a[rt].l&&a[right].ll<a[rt].r)){
a[rt].mov=tt;a[rt].l=a[left].rr;a[rt].r=a[right].ll;
}
}
void build(int l,int r,int rt){
if(l==r){
scanf("%lld",&a[rt].sum);
a[rt].lsum=a[rt].rsum=a[rt].mov=a[rt].sum;//各种和都相等
a[rt].ll=a[rt].rr=a[rt].l=a[rt].r=l; //各种端点=l;
return ;
}
havemid;
build(lson);
build(rson);
pushup(l,r,rt);
}
node query(int L,int R,int l,int r,int rt){
if(L<=l&&r<=R){
return a[rt];
}
havemid;
if(L>m)return query(L,R,rson);
else if(R<=m)return query(L,R,lson);
else {
node t1=query(L,R,lson);
node t2=query(L,R,rson);
node ret;
if(t1.mov>=t2.mov)ret=t1;
else ret=t2;
ret.sum=t1.sum+t2.sum;
LL tt=t1.rsum+t2.lsum; //这么并可以保证在查询区间中
if(tt>ret.mov||(tt==ret.mov&&t1.rr<ret.l)||
(tt==ret.mov&&t1.rr==ret.l&&t2.ll<ret.r)){
ret.mov=tt;ret.l=t1.rr;ret.r=t2.ll;
}
ret.lsum=t1.lsum;
ret.rsum=t2.rsum;
ret.ll=t1.ll;
ret.rr=t2.rr;
if(t1.sum+t2.lsum>ret.lsum){
ret.lsum=t1.sum+t2.lsum;
ret.ll=t2.ll;
}
if(t2.sum+t1.rsum>ret.rsum){
ret.rsum=t2.sum+t1.rsum;
ret.rr=t1.rr;
}
return ret;
}
}
int main(){
int T,n,m,a,b;
scanf("%d",&T);
while(T--){
scanf("%d%d",&n,&m);
build(1,n,1);
while(m--){
scanf("%d%d",&a,&b);
node t=query(a,b,1,n,1);
printf("%d %d\n",t.l,t.r);
}
}
return 0;
}