UVALive 3938 - "Ray, Pass me the dishes!" - [最大连续子列和+线段树]
题目链接:https://cn.vjudge.net/problem/UVALive-3938
参考刘汝佳书上说的:
题意:
给出一个长度为n的序列, 再给出m个询问, 每个询问是在序列 $[a,b]$ 之间的最大连续和. 要你计算出这个这个区间内最大连续和的区间 $[x,y](a \le x \le y \le b)$;
思路:
1、构造线段树,其中每个结点维护3个值,最大连续子列和 $max\_sub$,最大前缀和 $max\_prefix$,最大后缀和 $max\_suffix$。
具体来说,
$max\_sub(a,b)$ 是满足 $a \le x \le y \le b$ 且 $D_x+D_{x+1}+…+D_y$ 最大的二元组 $(x,y)$;
$max\_prefix(a,b)$ 是满足 $a \le x \le b$ 且 $D_a+D_{a+1}+…+D_x$ 最大的整数 $x$;
$max\_suffix(a,b)$ 是满足 $a \le x \le b$ 且 $D_x+D_{x+1}+…+D_b$ 最大的整数 $x$;
例如 $n=64$,询问为 $(20,50)$,则线段 $[20,50]$ 在线段树的根结点处被分成了两条线段 $[20,32]$ 和 $[33,50]$。则 $max\_sub(20, 50)$ 的起点和终点有3种情况:
情况一:起点和终点都在 $[20,32]$ 中,则 $max\_sub(20,50)=max\_sub(20,32)$。
情况二:起点和终点都在 $[33,50]$ 中,则 $max\_sub(20,50)=max\_sub(33,50)$。
情况三:起点在 $[20,32]$ 中,终点在 $[33,50]$ 中,则 $max\_sub(20,50)=max\_suffix(20, 32) + max\_prefix(33,50)$。
类似地 $max\_suffix$ 和 $max\_prefix$ 也可以这样递推,建树的时间复杂度为 $O(n)$,单组查询的时间复杂度为 $O(\log n)$。
当然,我们实际上做的话,没这么简单:
我们每个线段树节点都维护以下几个值:
max_sub:最大连续子列和
max_pre:最大前缀和
max_suf:最大后缀和
pre_i:最大前缀的结束位置
suf_i:最大后缀的开始位置
sum:区间总和
根据上面刘汝佳书上的三种情况可以写出pushup()函数(前缀和、后缀和的更新也在包含里面),然后后面的build()和query()两个函数都可以用pushup()函数。
更多详细的情况,都在代码里体现了。
AC代码:
#include<cstdio>
#include<algorithm>
#define MAXN 500000+5
typedef long long ll;
using namespace std;
struct Node{
int l,r;
ll sum,max_sub,max_pre,max_suf;//区间和,最大连续子列和,最大前缀和,最大后缀和
int sub_l,sub_r,pre_i,suf_i;//最大连续子列和的边界,最大前缀和的边界,最大后缀和的边界
}node[*MAXN];
int n,m,a[MAXN];
void pushup(Node& root,Node& lchild,Node& rchild)
{
root.sum = lchild.sum + rchild.sum; if(lchild.max_pre >= lchild.sum + rchild.max_pre)
{
root.max_pre = lchild.max_pre;
root.pre_i = lchild.pre_i;
}
else
{
root.max_pre = lchild.sum + rchild.max_pre;
root.pre_i = rchild.pre_i;
} if(lchild.max_suf + rchild.sum >= rchild.max_suf)
{
root.max_suf = lchild.max_suf + rchild.sum;
root.suf_i = lchild.suf_i;
}
else
{
root.max_suf = rchild.max_suf;
root.suf_i = rchild.suf_i;
} root.max_sub = lchild.max_sub;
root.sub_l = lchild.sub_l;
root.sub_r = lchild.sub_r;
if(lchild.max_suf + rchild.max_pre > root.max_sub || (lchild.max_suf + rchild.max_pre == root.max_sub && lchild.suf_i < root.sub_l))
{
root.max_sub = lchild.max_suf + rchild.max_pre;
root.sub_l = lchild.suf_i;
root.sub_r = rchild.pre_i;
}
if(rchild.max_sub > root.max_sub)
{
root.max_sub = rchild.max_sub;
root.sub_l = rchild.sub_l;
root.sub_r = rchild.sub_r;
}
}
void build(int root,int l,int r)
{
node[root].l=l;
node[root].r=r;
if(l==r)
{
node[root].sum=a[l];
node[root].max_sub=a[l]; node[root].sub_l=l; node[root].sub_r=l;
node[root].max_pre=a[l]; node[root].pre_i=l;
node[root].max_suf=a[l]; node[root].suf_i=l;
}
else
{
int mid=l+(r-l)/;
build(root*,l,mid);
build(root*+,mid+,r);
pushup(node[root],node[root*],node[root*+]);
}
}
Node query(int root,int st,int ed)
{
if(st==node[root].l && node[root].r==ed) return node[root]; int mid=(node[root].l+node[root].r)/;
if(ed<=mid) return query(root*,st,ed);
else if(st>mid) return query(root*+,st,ed);
else
{
Node r1=query(root*,st,mid);
Node r2=query(root*+,mid+,ed);
Node ans; ans.l = st, ans.r=ed;
pushup(ans,r1,r2); return ans;
}
}
int main()
{
int kase=;
while(scanf("%d%d",&n,&m)!=EOF)
{
for(int i=;i<=n;i++) scanf("%d",&a[i]);
build(,,n);
printf("Case %d:\n",++kase);
for(int i=,l,r;i<=m;i++)
{
scanf("%d%d",&l,&r);
Node ans=query(,l,r);
printf("%d %d\n",ans.sub_l,ans.sub_r);
}
}
}
PS.这道题可以说要对普通线段树模板进行巨大的改动,是一道可以加深对线段树理解的好题。
UVALive 3938 - "Ray, Pass me the dishes!" - [最大连续子列和+线段树]的更多相关文章
- uvalive 3938 "Ray, Pass me the dishes!" 线段树 区间合并
题意:求q次询问的静态区间连续最大和起始位置和终止位置 输出字典序最小的解. 思路:刘汝佳白书 每个节点维护三个值 pre, sub, suf 最大的前缀和, 连续和, 后缀和 然后这个题还要记录解的 ...
- UVALive 3938 Ray, Pass me the dishes! (动态最大连续和)
题意:求一个动态区间的最大连续和. 静态版本的O(n)算法显示不适用了,但是可以用线段树分治,因为一个连续和要在两边的区间,要么跨越两边,对于一个结点维护最大前缀和,后缀和,子区间连续和. 题目要求输 ...
- UvaLA 3938 "Ray, Pass me the dishes!"
"Ray, Pass me the dishes!" Time Limit: 3000MS Memory Limit: Unkn ...
- UVA 1400."Ray, Pass me the dishes!" -分治+线段树区间合并(常规操作+维护端点)并输出最优的区间的左右端点-(洛谷 小白逛公园 升级版)
"Ray, Pass me the dishes!" UVA - 1400 题意就是线段树区间子段最大和,线段树区间合并,但是这道题还要求输出最大和的子段的左右端点.要求字典序最小 ...
- UVA 1400 1400 - "Ray, Pass me the dishes!"(线段树)
UVA 1400 - "Ray, Pass me the dishes!" option=com_onlinejudge&Itemid=8&page=show_pr ...
- 【LA3938】"Ray, Pass me the dishes!"
原题链接 Description After doing Ray a great favor to collect sticks for Ray, Poor Neal becomes very hun ...
- UVALive - 3938:"Ray, Pass me the dishes!"
优美的线段树 #include<cstdio> #include<cstdlib> #include<algorithm> #include<cstring& ...
- 线段树(区间合并) LA 3989 "Ray, Pass me the dishes!"
题目传送门 题意:动态最大连续子序列和,静态的题目 分析:nlogn的归并思想.线段树维护结点的三个信息,最大前缀和,最大后缀和,该区间的最大和的两个端点,然后答案是三个的better.书上用pair ...
- UVa 1400 (线段树) "Ray, Pass me the dishes!"
求一个区间的最大连续子序列,基本想法就是分治,这段子序列可能在区间的左半边,也可能在区间的右半边,也有可能是横跨区间中点,这样就是左子区间的最大后缀加上右子区间的最大前缀之和. 线段树维护三个信息:区 ...
随机推荐
- Android 程序打包及签名(转)
为什么要签名??? 开发Android的人这么多,完全有可能大家都把类名,包名起成了一个同样的名字,这时候如何区分?签名这时候就是起区分作用的. 由于开发商可能通过使用相同的Package Name来 ...
- 解决app频繁更新方案
目前由于我们项目的特定场景,app的主要问题在于如何做到可以频繁更新而不需要频繁发布,尤其是ios,发布app store的周期基本上是2周左右,虽然可以通过企业号解决,但是后期的打包,分发给用户仍较 ...
- Splash 简介与安装
Splash 说白了就是一个轻量级的浏览器,利用它,我们同样可以实现跟其他浏览器一样的操作,我们使用 Docker 来安装 Splash: [root@localhost ~]# docker run ...
- 团购已满,O2O只是个笑话吗?
团购的用户习惯经过多年的“发酵”以后,大多数用户的团购习惯已经养成,同样一张电影票团购和直接现场购买的差价在一倍以上,当然O2O领域的其他情况也差不多,面对明显的“优惠”,用户当然乐意使用团购服务. ...
- Python生成器笔记
Python中三大器有迭代器,生成器,装饰器,本文主要讲述生成器.主要从生成器的概念,本质,以及yield关键字的使用执行过程. 本质:生成器是一类特殊的迭代器,使用了yield关键字的函数不再是函数 ...
- JavaScript Promise迷你书(中文版)
最近,发现了一个很不错的关于Promise介绍的迷你电子版书,分享给大家: http://liubin.org/promises-book/#chapter4-advanced-promise (篇幅 ...
- .NET 正则表达式使用高级技巧之替换类介绍
因为.net的基本正则语法和Perl5基本相同,所以基本语法你可以去下载一下M$的JS帮助文档,上面有详细的说明 \d表示什么,{,}表示什么,\[表示什么……,这里我只想提醒大家一点,为了避免和反向 ...
- Esper学习之十五:Pattern(二)
上一篇开始了新一轮语法——Pattern的讲解,一开始为大家普及了几个基础知识,其中有说到操作符.当时只是把它们都列举出来了,所以今天这篇就是专门详解这些操作符的,但是由于篇幅限制,本篇先会讲几个,剩 ...
- css笔记 - 张鑫旭css课程笔记之 padding 篇
[padding地址](https://www.imooc.com/learn/710) 一.padding与容器尺寸之间的关系 padding会影响元素的尺寸(通常情况下是通过增加/挤压内容区域) ...
- bootstrap 中这段代码 使bundles 失败
_:-ms-fullscreen, :root input[type="date"], _:-ms-fullscreen, :root input[type="time& ...