Tyvj 1518 CPU监控(线段树)
题目描述:
Bob需要一个程序来监视CPU使用率。这是一个很繁琐的过程,为了让问题更加简单,Bob会慢慢列出今天会在用计算机时做什么事。 Bob会干很多事,除了跑暴力程序看视频之外,还会做出去玩玩和用鼠标乱点之类的事,甚至会一脚踢掉电源……这些事有的会让做这件事的这段时间内CPU使用率增加或减少一个值;有的事还会直接让CPU使用率变为一个值。 当然Bob会询问:在之前给出的事件影响下,CPU在某段时间内,使用率最高是多少。有时候Bob还会好奇地询问,在某段时间内CPU曾经的最高使用率是多少。 为了使计算精确,使用率不用百分比而用一个整数表示。 不保证Bob的事件列表出了莫名的问题,使得使用率为负………………
输入格式:
第一行一个正整数T,表示Bob需要监视CPU的总时间。 然后第二行给出T个数表示在你的监视程序执行之前,Bob干的事让CPU在这段时间内每个时刻的使用率达已经达到了多少。 第三行给出一个数E,表示Bob需要做的事和询问的总数。 接下来E行每行表示给出一个询问或者列出一条事件: Q X Y:询问从X到Y这段时间内CPU最高使用率 A X Y:询问从X到Y这段时间内之前列出的事件使CPU达到过的最高使用率 P X Y Z:列出一个事件这个事件使得从X到Y这段时间内CPU使用率增加Z C X Y Z:列出一个事件这个事件使得从X到Y这段时间内CPU使用率变为Z 时间的单位为秒,使用率没有单位。 X和Y均为正整数(X<=Y),Z为一个整数。 从X到Y这段时间包含第X秒和第Y秒。 保证必要运算在有符号32位整数以内。
输出格式:
对于每个询问,输出一行一个整数回答。
没看懂题?没关系,将题目抽象化一下:
题目大意:
给出序列,要求查询一些区间的最大值、历史最大值,支持区间加、区间修改。序列长度(n)和操作数(m)<=1e5。
题解:
乍一看,这不线段树水题吗!其实不然,此题在洛谷上是黑题,模板很简单,难度在于标记下传。
我们将操作分开来看。首先,这颗线段树要支持区间查询最大值,这个很简单,然后要支持区间赋值和加值。
赋值和加值得结合让此题有了一些难度。对于任何一个点,不能同时拥有两个标记,否则会冲突,因为两个标记是相互影响的,所以操作的先后也就意味着答案。幸运的是,如果我们在每一步操作之前都先下传标记,那么就会解决冲突。
标记的下传是难点。我们先不考虑历史最大值,那么我们用到的标记只有两个,一个为ad,记录加值,另一个为se,记录赋值,两个标记不能同时出现,所以一次操作最多下传一个标记,只下传有信息的标记即可,下传时,由于子节点可能有自己的信息,所以要进行分类讨论:
1、若父节点有ad值,子节点有ad值,累加即可;
2、若父节点有ad值,子节点有se值,则在子节点的se值上加上ad;
3、若父节点有se值,子节点有ad值,则清空字节点的ad值,将子节点的色se值赋成父节点的se值;
4、若父节点有se值,子节点有se值,则用父节点的se值覆盖子节点的se值;
5、若子节点无信息,直接下传。
分的类别很多,但是我们可以将某些性质相似的情况合并,减少代码量,例如第5条就很好合并。对于原因作者不再过多陈述,应该很简单,想想就能明白。
至此我们解决了冲突问题。
然后,我们再考虑历史最大值的问题。
由于标记之打在最上面一层,所以标记的信息可能在传到子节点之前,就已经被覆盖掉。我们为了维护历史最大值,还要相对维护ad值和se值的历史最大值。用had和hse分别代表ad和se的历史最大值。
那么我们可以发现,当标记下传时,信息就可以更新到子节点,所以我们以标记下传为间隔维护had和hse。
历史最大值hma有如下几条来源:
1、ma被直接更新时的值;
2、标记下传时的hse值;
3、标记下传时had和当前ma值之和。
所以每次下传标记时,用以上三条更新即可,但要注意顺序,应先进行2、3再进行1,因为进行1时ma的值改变,其属性不再属于标记下传以前,所以会得到错误答案。
接下来便是had与hse的维护问题。
维护较为简单,我们只要已标记下传为间隔,每次用当前的ad之和se值与父节点的had和hse值更新。每次标记下传时,将had,ad,hse和se都清空,代表一个过程的结束。
had和hse可以由父节点的had和hse值转移,与上面5条类似,注意had和hse还可以直接由ad值和se值更新。
其实我们发现此操作可以分成四部分:
1、用父节点的ad更新;
2、用父节点的had更新;
3、用父节点的se更新;
4、用父节点的hse更新;
这四种操作可写为四个函数,进一步降低代码复杂度。在add和set的函数里,只需进行操作1和3,因为没有父节点的影响,在标记下传时,先进行2、4,再进行1、3,保证状态不会混乱。
要注意的是更新的状态层次,要保持一致,在此题中就是以标记下传分割层次,不要串层更新。
通俗的说,就是我用标记更新儿子,儿子就不会用标记更新自己。add和set函数内的更新其实就是标记下传的一部分,标记下传应与函数内的语句一致。
要注意将se和hse的初值赋为负极大值,排除0的干扰。
此题除了标记下传,其他语句都是正常的线段树打法,不要将问题复杂化。
还有,注意细节,这题打多了可过,打少了一定WA。
复杂度O(nlogn)
Code:
#include<iostream>
#include<cstdio>
using namespace std;
const int N=1e5+;
const int inf=;
int n,m;
struct seg{
int l,r;
int ma,hma;
int ad,had;
int se,hse;
}t[N<<];
char get()
{
char c=getchar();
while(c!='A'&&c!='C'&&c!='P'&&c!='Q')
c=getchar();
return c;
}
void pushup(int k)
{
t[k].ma=max(t[k<<].ma,t[k<<|].ma);
t[k].hma=max(t[k<<].hma,t[k<<|].hma);
}
void pushdown(int k)
{
if(t[k].l!=t[k].r)
{
for(int i=;i<=;i++)
{
int ch=k<<|i;
t[ch].hma=max(t[ch].hma,t[ch].ma+t[k].had);
if(t[ch].hse!=-inf)
t[ch].hse=max(t[ch].hse,t[ch].se+t[k].had);
else
t[ch].had=max(t[ch].had,t[k].had+t[ch].ad);
t[ch].hma=max(t[ch].hma,t[k].hse);
t[ch].hse=max(t[ch].hse,t[k].hse);
if(t[k].ad!=)
{
t[ch].ma+=t[k].ad;
t[ch].hma=max(t[ch].hma,t[ch].ma);
if(t[ch].se==-inf)
{
t[ch].ad+=t[k].ad;
t[ch].had=max(t[ch].had,t[ch].ad);
}
else
{
t[ch].se+=t[k].ad;
t[ch].hse=max(t[ch].hse,t[ch].se);
}
}
else if(t[k].se!=-inf)
{
t[ch].ma=t[k].se;
t[ch].hma=max(t[ch].hma,t[ch].ma);
t[ch].se=t[k].se;
t[ch].hse=max(t[k].se,t[ch].hse);
t[ch].ad=;
}
}
}
t[k].se=t[k].hse=-inf;
t[k].ad=t[k].had=;
}
void build(int k,int l,int r)
{
t[k].l=l;
t[k].r=r;
if(l==r)
{
scanf("%d",&t[k].ma);
t[k].hma=t[k].ma;
t[k].ad=t[k].had=;
t[k].se=t[k].hse=-inf;
return;
}
int mid=(l+r)>>;
build(k<<,l,mid);
build(k<<|,mid+,r);
t[k].se=t[k].hse=-inf;
pushup(k);
}
void add(int k,int l,int r,int x)
{
pushdown(k);
if(t[k].l>=l&&t[k].r<=r)
{
t[k].ma+=x;
t[k].hma=max(t[k].hma,t[k].ma);
if(t[k].se==-inf)
{
t[k].ad+=x;
t[k].had=max(t[k].had,t[k].ad);
}
else
{
t[k].se+=x;
t[k].hse=max(t[k].hse,t[k].se);
}
return;
}
int mid=(t[k].l+t[k].r)>>;
if(l<=mid)
add(k<<,l,r,x);
if(r>mid)
add(k<<|,l,r,x);
pushup(k);
}
void change(int k,int l,int r,int x)
{
pushdown(k);
if(t[k].l>=l&&t[k].r<=r)
{
t[k].ma=x;
t[k].hma=max(t[k].hma,t[k].ma);
t[k].se=x;
t[k].hse=max(x,t[k].hse);
t[k].ad=;
return;
}
int mid=(t[k].l+t[k].r)>>;
if(l<=mid)
change(k<<,l,r,x);
if(r>mid)
change(k<<|,l,r,x);
pushup(k);
}
int que(int k,int l,int r)
{
pushdown(k);
if(t[k].l>=l&&t[k].r<=r)
return t[k].ma;
int mid=(t[k].l+t[k].r)>>;
if(r<=mid)
return que(k<<,l,r);
else if(l>mid)
return que(k<<|,l,r);
else
return max(que(k<<,l,mid),que(k<<|,mid+,r));
}
int queh(int k,int l,int r)
{
pushdown(k);
if(t[k].l>=l&&t[k].r<=r)
return t[k].hma;
int mid=(t[k].l+t[k].r)>>;
if(r<=mid)
return queh(k<<,l,r);
else if(l>mid)
return queh(k<<|,l,r);
else
return max(queh(k<<,l,mid),queh(k<<|,mid+,r));
}
int main()
{
scanf("%d",&n);
build(,,n);
scanf("%d",&m);
for(int i=;i<=m;i++)
{
int x,y,z;
char op=get();
if(op=='A')
{
scanf("%d%d",&x,&y);
printf("%d\n",queh(,x,y));
}
else if(op=='C')
{
scanf("%d%d%d",&x,&y,&z);
change(,x,y,z);
}
else if(op=='P')
{
scanf("%d%d%d",&x,&y,&z);
add(,x,y,z);
}
else
{
scanf("%d%d",&x,&y);
printf("%d\n",que(,x,y));
}
}
return ;
}
祝你们AC!
Tyvj 1518 CPU监控(线段树)的更多相关文章
- 【bzoj3064】Tyvj 1518 CPU监控 线段树维护历史最值
题目描述 给你一个序列,支持4种操作:1.查询区间最大值:2.查询区间历史最大值:3.区间加:4.区间赋值. 输入 第一行一个正整数T,表示Bob需要监视CPU的总时间. 然后第二行给出T个数表示在你 ...
- BZOJ3064 Tyvj 1518 CPU监控 线段树
欢迎访问~原文出处——博客园-zhouzhendong 去博客园看该题解 题目传送门 - BZOJ3064 题意概括 一个序列,要你支持以下操作: 1. 区间询问最大值 2. 区间询问历史最大值 3. ...
- [补档][Tyvj 1518]CPU监控
[Tyvj 1518]CPU监控 题目 Bob需要一个程序来监视CPU使用率.这是一个很繁琐的过程,为了让问题更加简单,Bob会慢慢列出今天会在用计算机时做什么事. Bob会干很多事,除了跑暴力程序看 ...
- Tyvj 1518 CPU监控——极恶线段树
题目大意: 给定一个区间及其各个元素的初值,要求支持如下操作: 1.区间加 2.区间赋值 3.查询区间最大值 4.查询区间历史最大值 分析: 容易想到线段树,但是细思恶极(仔细想想恶心到了极点)的是, ...
- 2018.07.27 bzoj3064: Tyvj 1518 CPU监控(线段树)
传送门 线段树好题. 维护区间加,区间覆盖,区间最大,区间历史最大. 这个东西在国家集训队2016论文集之<区间最值操作与历史最值问题--杭州学军中学 吉如一>中讲的已经很详细了. 简单来 ...
- bzoj3064 Tyvj 1518 CPU监控
Description Bob需要一个程序来监视CPU使用率.这是一个很繁琐的过程,为了让问题更加简单,Bob会慢慢列出今天会在用计算机时做什么事. Bob会干很多事,除了跑暴力程序看视频之外,还会做 ...
- CPU监控 线段树裸题
LINK:bzoj3064 此题甚好码了20min停下来思考的时候才发现不对的地方有点坑... 还真不好写来着 可这的确是线段树的裸题...我觉得我写应该没有什么大问题 不过思路非常的紊乱 如果是自己 ...
- BZOJ.3064.CPU监控(线段树 历史最值)
题目链接 \(Description\) 有一个长为n的序列Ai,要求支持查询[l,r]的最值.历史最值,区间加/重设 \(Solution\) 线段树,每个点再维护一个历史(从0到现在)最大值.历史 ...
- bzoj 3064: Tyvj 1518 CPU监控
Description 1.区间加 \(z\) 2.区间覆盖为 \(z\) 3.查询区间最大值 4.查询区间历史最大值 Solution 线段树维护历史最值,思想大致是维护标记出现过的最大值 考虑这种 ...
随机推荐
- 阿里云ssh免密登陆突然无效
[root@node03 ~]# ssh-copy-id node02 root@node02's password: sh: .ssh/authorized_keys: Permission den ...
- Missing artifact net.sf.json-lib:json-lib:jar:2.4
Missing artifact net.sf.json-lib:json-lib:jar:2.4 出现上述这种错误就是JAR没有引入进来 这时候发现是因为JDK版本的问题,所以需要在加一句 < ...
- spss-数据清洗-处理重复数据
spss-数据清洗-处理重复数据 数据导入之后就需要对数据进行清洗.数据清洗主要是对多余重复的数据筛选清除,将缺失的数据补充完整,将错误的数据纠正或者删除.接下来操作如何将重复数据处理操作. 步骤一: ...
- 大数据学习笔记之Hadoop(一):Hadoop入门
文章目录 大数据概论 一.大数据概念 二.大数据的特点 三.大数据能干啥? 四.大数据发展前景 五.企业数据部的业务流程分析 六.企业数据部的一般组织结构 Hadoop(入门) 一 从Hadoop框架 ...
- java 并发——ReentrantLock
java 并发--ReentrantLock 简介 public class ReentrantLock implements Lock, java.io.Serializable { // 继承了 ...
- (1.3)学习笔记之mysql体系结构(C/S整体架构、内存结构、物理存储结构、逻辑结构)
目录 1.学习笔记之mysql体系结构(C/S架构) 2.mysql整体架构 3.存储引擎 4.sql语句处理--SQL层(内存层) 5.服务器内存结构 6.mysql如何使用磁盘空间 7.mysql ...
- RegionServer Splitting Implementation:regionServer 分裂过程分析
图片: RegionServer Split Process The RegionServer decides locally to split the region, and prepares th ...
- Mybatis缓存1----系统缓存及简单配置介绍
mybatis缓存 系统缓存:常用的一级缓存和二级缓存 一级缓存 一级缓存是SqlSession级别的缓存,在操作数据库时需要构建SqlSession对象,在对象中有一个数据结构用于存储缓存数据. ...
- spark性能调优02-JVM调优
1.降低cache操作的内存占比 1.1 为什么要降低cache操作的内存占比 spark的堆内存分别两部分,一部分用来给Rdd的缓存进行使用,另一部分供spark算子函数运行使用,存放函数中的对象 ...
- HDU 1261 字串数(排列组合)
字串数 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)Total Submiss ...