UVA1400 Ray, Pass me the dishes! 【线段树 区间合并】

2024-01-20 14:38

本文主要是介绍UVA1400 Ray, Pass me the dishes! 【线段树 区间合并】,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

"Ray, Pass me the dishes!" UVA - 1400 

https://vjudge.net/problem/UVA-1400

 

题意

给出一个长度为n的整数序列D,对m个询问做出回答,对询问(a,b)找到(x,y)使得a<=x<=y<=b且Dx+Dx+1+……+Dy最大。如有多组答案取字典序最小的一组。

题解

sum[i]记录结点i控制的区间[l,r]中区间和最大的区间 
lsum[i]记录结点i控制的区间[l,r]中,从l开始往后最大连续区间和的区间右端点 
rsum[i]记录结点i控制的区间[l,r]中,从r开始往前最大连续区间和的区间左端点 
pre[N]存放初始数组的前缀和

C++代码

#include<iostream>
#include<utility>
#include<algorithm>using namespace std;#define ls l,m,rt<<1
#define rs m+1,r,rt<<1|1typedef long long ll;
typedef pair<int,int> interval;const int N=500500;interval sum[N<<2];//sum[i]记录结点i控制的区间[l,r]中区间和最大的区间 
int lsum[N<<2];//lsum[i]记录结点i控制的区间[l,r]中,从l开始往后最大连续区间和的区间右端点 
int rsum[N<<2];//rsum[i]记录结点i控制的区间[l,r]中,从r开始往前最大连续区间和的区间左端点 
ll pre[N];//存放初始数组的前缀和//获取区间[l,r]的元素和 
ll get_sum(int l,int r)
{return pre[r]-pre[l-1];
}ll get_sum(interval a)
{return get_sum(a.first,a.second);
}//比较两个区间的区间和,返回区间和大的区间
//如果区间和一样,返回左端点小的,左端点一样则返回右端点小的 
interval better(interval a,interval b)
{ll v1=get_sum(a),v2=get_sum(b);if(v1==v2)return a<b?a:b;elsereturn v1<v2?b:a;
}//根据左右孩子结点的信息更新父亲结点的信息 
void pushup(int l,int r,int rt)
{//最大和的区间sum[rt]=better(sum[rt<<1],sum[rt<<1|1]); sum[rt]=better(sum[rt],make_pair(rsum[rt<<1],lsum[rt<<1|1]));//前缀延深的最长距离(从l位置往后),记录右端点 ll v1=get_sum(l,lsum[rt<<1]);ll v2=get_sum(l,lsum[rt<<1|1]);lsum[rt]=(v2>v1?lsum[rt<<1|1]:lsum[rt<<1]);//后缀延深的最长距离(从r位置往前),记录左端点v1=get_sum(rsum[rt<<1|1],r);v2=get_sum(rsum[rt<<1],r);rsum[rt]=(v2>=v1?rsum[rt<<1]:rsum[rt<<1|1]); 
}void build(int l,int r,int rt)
{if(l==r){sum[rt]=make_pair(l,r);lsum[rt]=rsum[rt]=l;return;}int m=(l+r)>>1;build(ls);build(rs);pushup(l,r,rt);
}//调用此函数的前提是l在区间[L,R]中
//求以l为起始元素,终止元素也在[L,R]中其值最大的连续字段和的区间 
interval lquery(int L,int R,int l,int r,int rt) 
{if(lsum[rt]<=R) return make_pair(l,lsum[rt]);int m=(l+r)>>1;if(R<=m) return lquery(L,R,ls);interval a=lquery(L,R,rs);a.first=l;return better(a,make_pair(l,lsum[rt<<1]));
}//调用此函数的前提是r在区间[L,R]中
//求以r为终止元素,起始元素也在[L,R]中其值最大的连续字段和的区间 
interval rquery(int L,int R,int l,int r,int rt) 
{if(rsum[rt]>=L) return make_pair(rsum[rt],r);int m=(l+r)>>1;if(m<L) return rquery(L,R,rs);interval a=rquery(L,R,ls);a.second=r;return better(a,make_pair(rsum[rt<<1|1],r));
}interval query(int L,int R,int l,int r,int rt)
{if(L<=l&&r<=R) return sum[rt];int m=(l+r)>>1;if(R<=m) return query(L,R,ls);//在左子树 if(m<L) return query(L,R,rs);//在右子树//在中间int a=rquery(L,R,ls).first;int b=lquery(L,R,rs).second;//printf("a=%d b=%d\n",a,b);interval ans=better(query(L,R,ls),query(L,R,rs));//最大子段和完全在左边和完全在右边//最大子段和在中间return better(ans,make_pair(a,b)); 
}int main()
{int n,m,t=1,x,L,R;while(~scanf("%d%d",&n,&m)) {pre[0]=0;//前缀和for(int i=1;i<=n;i++){scanf("%d",&x);pre[i]=pre[i-1]+x;}build(1,n,1);printf("Case %d:\n",t++);while(m--){scanf("%d%d",&L,&R);interval ans=query(L,R,1,n,1);printf("%d %d\n",ans.first,ans.second);} }return 0;
}

 

这篇关于UVA1400 Ray, Pass me the dishes! 【线段树 区间合并】的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/626333

相关文章

基于C#实现PDF文件合并工具

《基于C#实现PDF文件合并工具》这篇文章主要为大家详细介绍了如何基于C#实现一个简单的PDF文件合并工具,文中的示例代码简洁易懂,有需要的小伙伴可以跟随小编一起学习一下... 界面主要用于发票PDF文件的合并。经常出差要报销的很有用。代码using System;using System.Col

Python视频剪辑合并操作的实现示例

《Python视频剪辑合并操作的实现示例》很多人在创作视频时都需要进行剪辑,本文主要介绍了Python视频剪辑合并操作的实现示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习... 目录介绍安装FFmpegWindowsMACOS安装MoviePy剪切视频合并视频转换视频结论介绍

不删数据还能合并磁盘? 让电脑C盘D盘合并并保留数据的技巧

《不删数据还能合并磁盘?让电脑C盘D盘合并并保留数据的技巧》在Windows操作系统中,合并C盘和D盘是一个相对复杂的任务,尤其是当你不希望删除其中的数据时,幸运的是,有几种方法可以实现这一目标且在... 在电脑生产时,制造商常为C盘分配较小的磁盘空间,以确保软件在运行过程中不会出现磁盘空间不足的问题。但在

在C#中合并和解析相对路径方式

《在C#中合并和解析相对路径方式》Path类提供了几个用于操作文件路径的静态方法,其中包括Combine方法和GetFullPath方法,Combine方法将两个路径合并在一起,但不会解析包含相对元素... 目录C#合并和解析相对路径System.IO.Path类幸运的是总结C#合并和解析相对路径对于 C

hdu2241(二分+合并数组)

题意:判断是否存在a+b+c = x,a,b,c分别属于集合A,B,C 如果用暴力会超时,所以这里用到了数组合并,将b,c数组合并成d,d数组存的是b,c数组元素的和,然后对d数组进行二分就可以了 代码如下(附注释): #include<iostream>#include<algorithm>#include<cstring>#include<stack>#include<que

poj3468(线段树成段更新模板题)

题意:包括两个操作:1、将[a.b]上的数字加上v;2、查询区间[a,b]上的和 下面的介绍是下解题思路: 首先介绍  lazy-tag思想:用一个变量记录每一个线段树节点的变化值,当这部分线段的一致性被破坏我们就将这个变化值传递给子区间,大大增加了线段树的效率。 比如现在需要对[a,b]区间值进行加c操作,那么就从根节点[1,n]开始调用update函数进行操作,如果刚好执行到一个子节点,

hdu1394(线段树点更新的应用)

题意:求一个序列经过一定的操作得到的序列的最小逆序数 这题会用到逆序数的一个性质,在0到n-1这些数字组成的乱序排列,将第一个数字A移到最后一位,得到的逆序数为res-a+(n-a-1) 知道上面的知识点后,可以用暴力来解 代码如下: #include<iostream>#include<algorithm>#include<cstring>#include<stack>#in

hdu1689(线段树成段更新)

两种操作:1、set区间[a,b]上数字为v;2、查询[ 1 , n ]上的sum 代码如下: #include<iostream>#include<algorithm>#include<cstring>#include<stack>#include<queue>#include<set>#include<map>#include<stdio.h>#include<stdl

hdu 1754 I Hate It(线段树,单点更新,区间最值)

题意是求一个线段中的最大数。 线段树的模板题,试用了一下交大的模板。效率有点略低。 代码: #include <stdio.h>#include <string.h>#define TREE_SIZE (1 << (20))//const int TREE_SIZE = 200000 + 10;int max(int a, int b){return a > b ? a :

hdu 1166 敌兵布阵(树状数组 or 线段树)

题意是求一个线段的和,在线段上可以进行加减的修改。 树状数组的模板题。 代码: #include <stdio.h>#include <string.h>const int maxn = 50000 + 1;int c[maxn];int n;int lowbit(int x){return x & -x;}void add(int x, int num){while