LA3938 "Ray, Pass me the dishes!" (线段树区间合并)

题目:http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=22105

题意:给定整数n和m,给出一个n个元素的序列,查询m次给定区间[L,R]的最大连续和的位置[x,y],有多个区间输出x最小的,还有多个的话输出y最小的。

分析:每个节点存8个信息,最大连续和、最大后缀和、最大前缀和、区间和、前缀末位置、后缀首位置、最大连续和的首位置和末位置。

最大连续和=max(lson最大连续和,rson最大连续和,lson最大后缀+rson最大前缀)

最大前缀和=max(lson最大前缀,lson区间和+rson最大前缀和)

最大后缀和=max(rson最大后缀,lson最大后缀+rson区间和)

查询的时候将节点合并就行了。

代码:

#include <iostream>
#include <cstdio>
using namespace std;
const int maxn = 5e5+6;
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
struct node
{
	long long Sum,MaxSum;
	long long Suf,Pre;
	int pos_p,pos_s,s_l,s_r;
}ans;
struct segtree
{
	node tree[maxn<<2];
	node U(node &a,node &b)
	{
		node ret;
		if(a.MaxSum>=b.MaxSum) //以下修改最大连续和,及标记位置
		{
			ret.MaxSum=a.MaxSum;
			ret.s_l=a.s_l;
			ret.s_r=a.s_r;
		}
		else
		{
			ret.MaxSum=b.MaxSum;
			ret.s_l=b.s_l;
			ret.s_r=b.s_r;
		}
		if(ret.MaxSum<a.Suf+b.Pre || (ret.MaxSum==a.Suf+b.Pre && a.pos_s<ret.s_l) ||
		   (ret.MaxSum==a.Suf+b.Pre && ret.s_l==a.pos_s && ret.s_r>b.pos_p))
		{
			ret.MaxSum=a.Suf+b.Pre;
			ret.s_l=a.pos_s;
			ret.s_r=b.pos_p;
		}
		if(a.Pre>=a.Sum+b.Pre) //以下修改最大前缀,及标记位置
		{
			ret.Pre=a.Pre;
			ret.pos_p=a.pos_p;
		}
		else
		{
			ret.Pre=a.Sum+b.Pre;
			ret.pos_p=b.pos_p;
		}
		if(b.Suf>a.Suf+b.Sum) //以下修改最大后缀,及标记位置
		{
			ret.Suf=b.Suf;
			ret.pos_s=b.pos_s;
		}
		else
		{
			ret.Suf=a.Suf+b.Sum;
			ret.pos_s=a.pos_s;
		}
		ret.Sum=a.Sum+b.Sum;  //修改区间和
		return ret;
	}
	void build(int l,int r,int rt)
	{
		if(l==r)
		{
			scanf("%lld",&tree[rt].Pre);  //坑啊C++11不能I64d
			tree[rt].MaxSum=tree[rt].Sum=tree[rt].Suf=tree[rt].Pre;
			tree[rt].pos_p=tree[rt].pos_s=tree[rt].s_l=tree[rt].s_r=r;
			return ;
		}
		int m=(l+r)>>1;
		build(lson);
		build(rson);
		tree[rt]=U(tree[rt<<1],tree[rt<<1|1]);
	}
	node query(int L,int R,int l,int r,int rt)
	{
		if(L<=l && r<=R)
			return tree[rt];
		int m=(l+r)>>1,fg1(0),fg2(0);
		node ans1,ans2;
		if(L<=m)
			ans1=query(L,R,lson),fg1=1;
		if(R>m)
			ans2=query(L,R,rson),fg2=1;
		if(!fg1)
			return ans2;
		if(!fg2)
			return ans1;
		return U(ans1,ans2);
	}
}T;
int main()
{
	int n,m,x,y,ncase=1;
	while(scanf("%d%d",&n,&m)!=EOF)
	{
		T.build(1,n,1);
		printf("Case %d:\n",ncase++);
		while(m--)
		{
			scanf("%d%d",&x,&y);
			ans=T.query(x,y,1,n,1);
			printf("%d %d\n",ans.s_l,ans.s_r);
		}
	}
	return 0;
}

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-08-08 13:48:17

LA3938 "Ray, Pass me the dishes!" (线段树区间合并)的相关文章

uvalive 3938 &quot;Ray, Pass me the dishes!&quot; 线段树 区间合并

题意:求q次询问的静态区间连续最大和起始位置和终止位置 输出字典序最小的解. 思路:刘汝佳白书 每个节点维护三个值 pre, sub, suf 最大的前缀和, 连续和, 后缀和 然后这个题还要记录解的位置所以还要区间总和sum 1 #include<iostream> 2 #include<string> 3 #include<algorithm> 4 #include<cstdlib> 5 #include<cstdio> 6 #include

LA3938:&quot;Ray, Pass me the dishes!&quot;(线段树)

Description After doing Ray a great favor to collect sticks for Ray, Poor Neal becomes very hungry. In return for Neal's help, Ray makes a great dinner for Neal. When it is time for dinner, Ray arranges all the dishes he makes in a single line (actua

UVALive3938 &quot;Ray, Pass me the dishes!&quot; 线段树动态区间最大和

AC得相当辛苦的一道题,似乎不难,但是需要想仔细, 开始的时候的错误思路----是受之前做过的区间最长连续子串影响http://blog.csdn.net/u011026968/article/details/38357157 区间合并的时候,我直接按照---如果(左子树的最大前缀和长度==左子树的长度 && 右子树的前缀和>0),就合并左前缀,这想法有两个错误:1.右子树的前缀和==0的时候是不是要合并区间呢?题目要求x尽量小,如果x相同y尽量小(x是ans的区间左端点,y是Ans

UVA 1400 1400 - &quot;Ray, Pass me the dishes!&quot;(线段树)

UVA 1400 - "Ray, Pass me the dishes!" 题目链接 题意:给定一个序列,每次询问一个[L,R]区间,求出这个区间的最大连续子序列和 思路:线段树,每个节点维护3个值,最大连续子序列,最大连续前缀序列,最大连续后缀序列,那么每次pushup的时候,根据这3个序列去拼凑得到新的一个结点即可 代码: #include <cstdio> #include <cstring> #include <algorithm> usin

uva 1400 - &quot;Ray, Pass me the dishes!&quot;(线段树)

题目链接:uva 1400 - "Ray, Pass me the dishes!" 题目大意:给定一个长度为n个整数序列,对m次询问作出回答,对于每次询问(a,b),找到两个下标x,y使得x到y的连续和为区间a,b中最大的连续和,如果存在多解优先x小,然后y小. 解题思路:线段树,对于每个节点维护三个线段值: max_sub:区间连续最大和 max_prefix:区间连续前缀最大和 max_suffix:区间连续后缀最大和 建树的过程维护三个值,查询时只需考虑左右子节点的max_su

Uva 1400 &quot;Ray, Pass me the dishes!&quot; ( 线段树 + 区间查询 )

Uva  1400 "Ray, Pass me the dishes!" (线段树 + 区间查询) 题意: 给顶一个长度为n的整数序列D,我们的任务是对m的询问做出回答对于询问(a,b),需要找到两个下标x和y,是的 a <= x <= y <=b并且Dx+...........Dy 尽量大. x,y尽量小 分析: 这题是做线段树比较好的一题,大白书上介绍的是维护了三个域,maxsub,maxpre,maxsuf这里也可以只维护两个域,再最后再考虑跨区间的问题这里没有

UVA - 1400&quot;Ray, Pass me the dishes!&quot;(线段树)

题目链接 题目大意:给你N个数字,要求你动态的给出L到R之间,X>= L && Y<=R,使得X,Y这段的连续和是LR之间的最大连续和,如果有多解,输出X小的,接着是Y小的. 解题思路:结点保存三个附加线段,max_sub, max_suffix, max_prefix.对于每次查询最大的连续和要不出现在左子树的max_sub, 要不就是右子树的max_sub, 要不就是左子树的max_suffix + 右子树的max_prefix.然后每次查询的时候都返回一个新的节点,是新的

HDU 3911 Black And White(线段树区间合并)

Problem Description There are a bunch of stones on the beach; Stone color is white or black. Little Sheep has a magic brush, she can change the color of a continuous stone, black to white, white to black. Little Sheep like black very much, so she wan

HDU 3308 LCIS (线段树区间合并)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3308 题目很好懂,就是单点更新,然后求区间的最长上升子序列. 线段树区间合并问题,注意合并的条件是a[mid + 1] > a[mid],写的细心点就好了. 1 #include <iostream> 2 #include <cstring> 3 #include <cstdio> 4 using namespace std; 5 const int MAXN = 1