题目链接 UVA1400
【题意】给出长度为n(<=500000)的序列,有m(<=500000)个查询(a,b),对于每个查询,需要找到下标x,y使得a<=x<=y<=b;并且[x,y]区间元素之和最大。
【分析】用线段树来做,每个线段树的节点添加max_all([a,b]区间的最大和,即所求答案),max_pre([a,b]区间最大前缀和),max_suf([a,b]区间最大后缀和),这样max_all只有三种情况(假设n = 60,[20,50]为查询区间,当前节点为根节点,则区间[10,60]被分为[10,30]和[31,60]):
一:起点终点都在[20,30],则max_all(20,50) = max_all(20,30);
二:起点终点都在[31,50],则max_all(20,50) = max_all(31,50);
三:起点在[20,30]终点在[31,50],则max_all(20,50) = max_suf(20,30)+max_pre(31,50)
这里可以用一个sum[]记录输入序列的前缀和,这样只需要记录max_suf的(l,r)区间下标,max_pre的终点px以及max_suf的起点sx即可通过下标计算出每个区间内的和。
这样max_pre和max_suf就好算了。
还有一个小技巧,可以用pair<int,int>来记录max_all的区间坐标,这样只要通过<号就可以直接比较出x,y尽量小(题目要求有多解时先输出x小的,还有多解再输出y小的)
【AC CODE】242ms
#include <cstdio>
#include <cstring>
#include <cmath>
#include <map>
#include <queue>
#include <stack>
#include <vector>
#include <string>
#include <algorithm>
using namespace std;
typedef long long LL;
#define rep(i,a,n) for(int i = a; i < n; i++)
#define repe(i,a,n) for(int i = a; i <= n; i++)
#define per(i,n,a) for(int i = n; i >= a; i--)
#define clc(a,b) memset(a,b,sizeof(a))
#define INF 0x3f3f3f3f3f3f3f3f
#define MAXN 500010
#define lc u<<1
#define rc u<<1|1
typedef pair<int, int> P;//存放线段树当前节点区间[x,y]中最大区间和的范围
struct NODE{//prex是区间[x,y]中的前缀最大和的位置,suf是后缀的
int x,y,prex,sufx;
P all;
}node[MAXN<<1];
LL sum[MAXN];
inline P biger(const P& a, const P& b){
LL suma = sum[a.second]-sum[a.first-1],sumb = sum[b.second]-sum[b.first-1];
return (suma > sumb || (suma == sumb && a<b)) ? a:b;
}
NODE pushup(const NODE& a, const NODE& b)
{
NODE ans;
ans.x = a.x, ans.y = b.y;
//更新all
ans.all = biger(a.all, b.all);//MAX起点终点都在[x,m]或者[m+1,y]
ans.all = biger(ans.all, P(a.sufx,b.prex));//MAX起点终点跨越m
//更新prex
ans.prex = biger(P(a.x,a.prex), P(a.x,b.prex)).second;
//更新sufx
ans.sufx = biger(P(a.sufx,b.y), P(b.sufx, b.y)).first;
return ans;
}
void bulid(int u, int x, int y)
{
if(x == y)
{
node[u].all=P(x,y);
node[u].x = node[u].y = node[u].prex = node[u].sufx = x;
return;
}
int m = (x+y)>>1;
bulid(lc,x,m);
bulid(rc,m+1,y);
node[u] = pushup(node[lc], node[rc]);
}
int ql,qr;//查询区间
NODE query(int u)
{
if(ql <= node[u].x && node[u].y <= qr) return node[u];
int m = (node[u].x+node[u].y)>>1;
NODE ans;
if(ql <= m && qr > m)//查询区间跨过m结点
ans = pushup(query(lc), query(rc));
else if(ql <= m) ans = query(lc);//查询区间在[x,m]
else if(qr > m) ans = query(rc);//查询区间在[m+1,y]
return ans;
}
LL in()
{
int ans,ch,f = 1;
while((ch = getchar()) < '0' || ch > '9') if('-' == ch){ch = getchar(),f = -1;break;}
ans = ch-'0';
while((ch = getchar()) >= '0' && ch <= '9') ans = (ans<<3)+(ans<<1)+ch-'0';
return f*ans;
}
int main()
{
#ifdef SHY
freopen("e:\\1.txt", "r", stdin);
#endif
int n,q, count = 0;
while(~scanf("%d %d%*c", &n, &q))
{
repe(i,1,n) sum[i] = sum[i-1]+in();
bulid(1,1,n);
printf("Case %d:\n", ++count);
rep(i,0,q)
{
scanf("%d %d%*c", &ql, &qr);
NODE ans = query(1);
printf("%d %d\n", ans.all.first, ans.all.second);
}
}
return 0;
}