LA3938 & UVA1400 - Ray, Pass me the dishes!(线段树)

题目链接  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;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值