题目链接 https://cn.vjudge.net/problem/UVALive-3938
【题意】
给定一个长度为n的整数序列D,你的任务是对m个询问作出回答。对于询问(a,b),需要找到两个下标x,y,使得a<=x<=y<=b,并且D(x)+D(x+1)+…+D(y)的值最大,如果有多解,要让x尽可能小,如果x确定后还有多解,要让y尽可能小。
【输入格式】
多组输入。每组数据第一行为两个整数n,m(1<=n,m<=500000)代表区间D的长度和查询次数,第二行包含n个整数,代表序列D的值,这些整数的绝对值小于1e9。接下来的m行,每行包含两个整数a,b,代表查询的左右区间。
【输出格式】
对每组数据,输出数据组数编号,然后每组查询输出一行x,y
【思路】
线段树区间合并问题,对序列D构造一颗线段树,维护4个值,pre,suf,ansx,ansy,分别代表最大前缀和下标,最大后缀和下标,还有要查询的答案的左右下标。这几个值都是可以递推求解的,在知道了左右子结点的所有信息后,当前结点的pre要么是左结点的pre,要么是右结点的pre,取前缀和更大的那一个,后缀和下标suf同理。然后就是要查询的答案ansx,ansy,首先它可以取左右结点的ansx,ansy里对应序列里和更大的一组,还要考虑的就是左结点的最大后缀和suf,和右结点的最大前缀和pre,这两段拼接起来形成的区间也有可能是最优解,需要进行比较,同时在答案相同的情况下保证x尽量小,y尽量小。查询时候的方法类似,把查询的区间分成左右区间递归查找答案。
#include
using namespace std;
typedef long long ll;
#define node tree[id]
#define lson tree[id<<1]
#define rson tree[id<<1|1]
const int maxn=500050;
struct Tree{
int left,right;
int ansx,ansy,pre,suf;
};
int n,m;
ll s[maxn];
Tree tree[maxn<<2];
void pushup(int id){
if(s[lson.pre]-s[node.left-1]>=s[rson.pre]-s[node.left-1]) node.pre=lson.pre;
else node.pre=rson.pre;
if(s[node.right]-s[lson.suf-1]>=s[node.right]-s[rson.suf-1]) node.suf=lson.suf;
else node.suf=rson.suf;
if(s[lson.ansy]-s[lson.ansx-1]>=s[rson.ansy]-s[rson.ansx-1]){
node.ansx=lson.ansx;
node.ansy=lson.ansy;
}
else{
node.ansx=rson.ansx;
node.ansy=rson.ansy;
}
if(s[rson.pre]-s[lson.suf-1]==s[node.ansy]-s[node.ansx-1]){
if(lson.suf.ansx){
node.ansx=lson.suf;
node.ansy=rson.pre;
}
else if(lson.suf==node.ansx && rson.pre.ansy){
node.ansy=rson.pre;
}
}
else if(s[rson.pre]-s[lson.suf-1]>s[node.ansy]-s[node.ansx-1]){
node.ansx=lson.suf;
node.ansy=rson.pre;
}
}
void build(int id,int le,int ri){
node.left=le;
node.right=ri;
if(le==ri){
node.ansx=node.ansy=node.pre=node.suf=le;
return;
}
int mid=(le+ri)>>1;
build(id<<1,le,mid);
build(id<<1|1,mid+1,ri);
pushup(id);
}
Tree query(int id,int x,int y){
if(x<=node.ansx && node.ansy<=y) return node;
if(y<=lson.right) return query(id<<1,x,y);
if(x>=rson.left) return query(id<<1|1,x,y);
int mid=(node.left+node.right)>>1;
Tree a=query(id<<1,x,mid);
Tree b=query(id<<1|1,mid+1,y);
Tree ans;
if(s[a.pre]-s[x-1]>=s[b.pre]-s[x-1]) ans.pre=a.pre;
else ans.pre=b.pre;
if(s[y]-s[a.suf-1]>=s[y]-s[b.suf-1]) ans.suf=a.suf;
else ans.suf=b.suf;
if(s[a.ansy]-s[a.ansx-1]>=s[b.ansy]-s[b.ansx-1]){
ans.ansx=a.ansx;
ans.ansy=a.ansy;
}
else{
ans.ansx=b.ansx;
ans.ansy=b.ansy;
}
if(s[b.pre]-s[a.suf-1]==s[ans.ansy]-s[ans.ansx-1]){
if(a.suf.ansx){
ans.ansx=a.suf;
ans.ansy=b.pre;
}
else if(a.suf==ans.ansx && b.pre.ansy){
ans.ansy=b.pre;
}
}
else if(s[b.pre]-s[a.suf-1]>s[ans.ansy]-s[ans.ansx-1]){
ans.ansx=a.suf;
ans.ansy=b.pre;
}
return ans;
}
int main(){
int kase=0;
while(scanf("%d%d",&n,&m)==2){
for(int i=1;i<=n;++i){
ll e;
scanf("%lld",&e);
s[i]=s[i-1]+e;
}
build(1,1,n);
printf("Case %d:\n",++kase);
while(m--){
int x,y;
scanf("%d%d",&x,&y);
Tree ans=query(1,x,y);
printf("%d %d\n",ans.ansx,ans.ansy);
}
}
return 0;
}