bzoj3064/洛谷P4314 CPU监控【线段树】
好,长草博客被催更了【?】
我感觉这题完全可以当作线段树3
线段树2考加法和乘法标记的下放顺序,这道题更丧心病狂【?】
很多人可能跟我一样,刚看到这道题秒出思路:打一个当前最大值一个历史最大值不就完事了吗
实际上这样做会死得很惨。节点保留的信息可能来不及下传就被父节点更新掉,导致一部分信息被覆盖而丢失,这样就有可能查不到正确的历史最大值。比如历史最大值是由add更新的,但是set覆盖下来把add变成了0。
为了保留这些信息,我们需要记录历史修改标记的最大值。标记hset(history-set)记录从上一次pushdown到现在set标记的最大值,hadd(history-add)记录从上一次pushdown到现在add标记达到过的最大值(不是add每次增加的最大值)。hset和hadd不是用来更新自身的hmax值的,而是用来在pushdown的时候下放,更新儿子的pushdown。仔细想一下就可以知道,当前节点保留的hset,hadd,与儿子存的各种当前信息(max,add,set)结合,都是儿子曾经能够达到的状态,更新合法且一定能更新出儿子准确的历史最大值。
因为pushdown的时候也要下放hset和hadd,所以只记录每次pushdown到现在的值。具体的下放顺序我写在代码注释里了。
#include<iostream>
#include<cstdio>
using namespace std;
int n,m;
char c[];
long long a[];
const long long inf=;//初始化用
struct node{
int l,r;
long long hmax,max,hadd,hset,add,set;//哪个代表什么很显然吧XD
}b[];
void build(int p,int l,int r){
b[p].l=l,b[p].r=r;
if(l==r){
b[p].hmax=b[p].max=a[l];
return;
}
int mid=(l+r)/;
b[p*].hset=b[p*].set=b[p*].hmax=b[p*].max=-inf;//初始化左右儿子,1的我写在主函数里了
b[p*+].hset=b[p*+].set=b[p*+].hmax=b[p*+].max=-inf;
build(p*,l,mid);
build(p*+,mid+,r);
b[p].max=max(b[p*].max,b[p*+].max);
b[p].hmax=max(b[p*].max,b[p*+].max);
}
void pushdown(int p){//核心操作,按顺序分类讨论
//写标记的习惯一般是,当前节点有标记=当前节点已经被更新好所有信息(hmax,max),
//保有可以下传而不对自己造成影响的修改标记(add,hadd,set,hset)
//pushdown的时候,让左右儿子也达到这个要求,自己清空所有修改标记,
//这样自己在成为修改函数最终目标的时候就可以直接更新所有信息。
b[p*].hmax=max(b[p*].hmax,max(b[p].hset,b[p].hadd+b[p*].max));//先用儿子在这次pushdown之前就保留的信息更新历史最大值
if(b[p*].set!=-inf)b[p*].hset=max(b[p*].hset,b[p*].set+b[p].hadd);//下放当前节点的hadd。儿子保有的set一定是在自身的hadd之前被打上的
else b[p*].hadd=max(b[p*].hadd,b[p].hadd+b[p*].add);//同理,儿子的add也是在自身的hadd之前被打上的
b[p*].hset=max(b[p*].hset,b[p].hset);//下放hset
if(b[p].add){//下放add
if(b[p*].set!=-inf)b[p*].set+=b[p].add;//如果儿子有set,就把add累计到set上
else b[p*].add+=b[p].add;
b[p*].max+=b[p].add;
}
if(b[p].set!=-inf){//下放set,直接覆盖
b[p*].max=b[p*].set=b[p].set;
b[p*].add=;//清零儿子的add,因为被直接覆盖掉了
}
b[p*].hset=max(b[p*].hset,b[p*].set);//用儿子更新过以后的信息来更新hset和hadd
b[p*].hadd=max(b[p*].hadd,b[p*].add); b[p*+].hmax=max(b[p*+].hmax,max(b[p].hset,b[p].hadd+b[p*+].max));//复制一遍
if(b[p*+].set!=-inf)b[p*+].hset=max(b[p*+].hset,b[p*+].set+b[p].hadd);
else b[p*+].hadd=max(b[p*+].hadd,b[p].hadd+b[p*+].add);
b[p*+].hset=max(b[p*+].hset,b[p].hset);
if(b[p].add){
if(b[p*+].set!=-inf)b[p*+].set+=b[p].add;
else b[p*+].add+=b[p].add;
b[p*+].max+=b[p].add;
}
if(b[p].set!=-inf){
b[p*+].max=b[p*+].set=b[p].set;
b[p*+].add=;
}
b[p*+].hset=max(b[p*+].hset,b[p*+].set);
b[p*+].hadd=max(b[p*+].hadd,b[p*+].add);
b[p].set=b[p].hset=-inf;
b[p].add=b[p].hadd=;
}
long long ask1(int p,int l,int r){//询问当前最大值,常规操作
if(b[p].l!=b[p].r)pushdown(p);
if(l<=b[p].l&&b[p].r<=r){
return b[p].max;
}
int mid=(b[p].l+b[p].r)/;
long long val=-inf;
if(l<=mid)val=max(val,ask1(p*,l,r));
if(r>mid)val=max(val,ask1(p*+,l,r));
b[p].max=max(b[p*].max,b[p*+].max);
b[p].hmax=max(b[p*].hmax,b[p*+].hmax);
return val;
}
long long ask2(int p,int l,int r){//询问历史最大值,常规操作
if(b[p].l!=b[p].r)pushdown(p);
if(l<=b[p].l&&b[p].r<=r){
return b[p].hmax;
}
int mid=(b[p].l+b[p].r)/;
long long val=-inf;
if(l<=mid)val=max(val,ask2(p*,l,r));
if(r>mid)val=max(val,ask2(p*+,l,r));
b[p].max=max(b[p*].max,b[p*+].max);
b[p].hmax=max(b[p*].hmax,b[p*+].hmax);
return val;
}
void change1(int p,int l,int r,long long val){//add
if(b[p].l!=b[p].r)pushdown(p);//到达之前就pushdown
if(l<=b[p].l&&b[p].r<=r){
b[p].add+=val;//因为已经pushdown过了,所以这里直接更新所有信息
b[p].hadd+=val;
b[p].max+=val;
b[p].hmax=max(b[p].hmax,b[p].max);
return;
}
int mid=(b[p].l+b[p].r)/;
if(l<=mid)change1(p*,l,r,val);
if(r>mid)change1(p*+,l,r,val);
b[p].max=max(b[p*].max,b[p*+].max);
b[p].hmax=max(b[p*].hmax,b[p*+].hmax);
}
void change2(int p,int l,int r,long long val){//set
if(b[p].l!=b[p].r)pushdown(p);//到达之前pushdown
if(l<=b[p].l&&b[p].r<=r){
b[p].max=b[p].hset=b[p].set=val;//同样,因为已经pushdown过了,直接更新所有信息
b[p].hmax=max(b[p].hmax,b[p].max);
return;
}
int mid=(b[p].l+b[p].r)/;
if(l<=mid)change2(p*,l,r,val);
if(r>mid)change2(p*+,l,r,val);
b[p].max=max(b[p*].max,b[p*+].max);
b[p].hmax=max(b[p*].hmax,b[p*+].hmax);
}
int main()//主函数都是常规操作
{
scanf("%d",&n);
for(int i=;i<=n;i++)scanf("%lld",&a[i]);
b[].hset=b[].set=b[].hmax=b[].max=-inf;
build(,,n);
scanf("%d",&m);
while(m--){
scanf("%s",c);
int x,y;
long long z;
if(c[]=='Q'){
scanf("%d%d",&x,&y);
printf("%lld\n",ask1(,x,y));
}
else if(c[]=='A'){
scanf("%d%d",&x,&y);
printf("%lld\n",ask2(,x,y));
}
else if(c[]=='P'){
scanf("%d%d%lld",&x,&y,&z);
change1(,x,y,z);
}
else{
scanf("%d%d%lld",&x,&y,&z);
change2(,x,y,z);
}
}
return ;
}
这道题真的非常有价值,希望看到的同学都不要放弃,努力写一下。就算是参考着题解写的也十分有意义_(:з」∠)_。
思路参考了洛谷题解区枫林晚的做法。
bzoj3064/洛谷P4314 CPU监控【线段树】的更多相关文章
- 洛谷题解P4314CPU监控--线段树
题目链接 https://www.luogu.org/problemnew/show/P4314 https://www.lydsy.com/JudgeOnline/problem.php?id=30 ...
- 【bzoj3064】Tyvj 1518 CPU监控 线段树维护历史最值
题目描述 给你一个序列,支持4种操作:1.查询区间最大值:2.查询区间历史最大值:3.区间加:4.区间赋值. 输入 第一行一个正整数T,表示Bob需要监视CPU的总时间. 然后第二行给出T个数表示在你 ...
- 【BZOJ】1012: [JSOI2008]最大数maxnumber /【洛谷】1198(线段树)
Description 现在请求你维护一个数列,要求提供以下两种操作:1. 查询操作.语法:Q L 功能:查询当前数列中末尾L个数中的最大的数,并输出这个数的值.限制:L不超过当前数列的长度.2. 插 ...
- 洛谷P3372/poj3468(线段树lazy_tag)(询问区间和,支持区间修改)
洛谷P3372 //线段树 询问区间和,支持区间修改 #include <cstdio> using namespace std; struct treetype { int l,r; l ...
- CPU监控 线段树裸题
LINK:bzoj3064 此题甚好码了20min停下来思考的时候才发现不对的地方有点坑... 还真不好写来着 可这的确是线段树的裸题...我觉得我写应该没有什么大问题 不过思路非常的紊乱 如果是自己 ...
- 洛谷P4065 [JXOI2017]颜色(线段树)
题意 题目链接 Sol 线段树板子题都做不出来,真是越来越菜了.. 根据题目描述,一个合法区间等价于在区间内的颜色没有在区间外出现过. 所以我们可以对于每个右端点,统计最长的左端点在哪里,刚开始以为这 ...
- 洛谷P5111 zhtobu3232的线段树
题意:给定线段树,上面若干个节点坏了,求能表示出多少区间. 区间能被表示出当且仅当拆出来的log个节点都是好的. 解:每个区间在最浅的节点处计算答案. 对于每个节点维护从左边过来能有多少区间,从右边过 ...
- BZOJ.3064.CPU监控(线段树 历史最值)
题目链接 \(Description\) 有一个长为n的序列Ai,要求支持查询[l,r]的最值.历史最值,区间加/重设 \(Solution\) 线段树,每个点再维护一个历史(从0到现在)最大值.历史 ...
- 洛谷P3960 列队 NOIp2017 线段树/树状数组/splay
正解:动态开点线段树 解题报告: 传送门! 因为最近学主席树的时候顺便get到了动态开点线段树?刚好想起来很久很久以前就想做结果一直麻油做的这题,,,所以就做下好了QAQ 然后说下,这题有很多种方法, ...
随机推荐
- ssoj 2279 磁力阵
说不想改最后还是向T1屈服了..然后就de了一下午Bug... 虽然昨天随口扯的有点道理,正解就是迭代加深A星搜索,但实际写起来就十分难受了. 说自己的做法,略鬼畜. 每个正方形的边界上的边.每条边在 ...
- zabbix自动发现主机(转)
zabbix自动发现主机 2018年06月15日 18:02:52 loyal-Wang 阅读数:817更多 个人分类: zabbix 版权声明:本文为博主原创文章,转载请注明出处. https: ...
- React中的this.props.children
React this.props.children this.props对象的属性与组件的属性一一对应,但是有一个例外,就是this.props.children属性.它表示组件的所有子节点. var ...
- Java-MyBatis-MyBatis3-XML映射文件:XML映射文件
ylbtech-Java-MyBatis-MyBatis3-XML映射文件:XML映射文件 1. XML 映射文件 MyBatis 的真正强大在于它的映射语句,这是它的魔力所在.由于它的异常强大,映射 ...
- 转:进程上下文VS中断上下文
源地址:http://www.cnblogs.com/zzx1045917067/archive/2012/12/19/2824552.html 内核空间和用户空间是现代操作系统的两种工作模式,内核模 ...
- ie浏览器下载附件中文乱码
String llq = request.getHeader( "USER-AGENT" ).toLowerCase();Boolean isIE = false;if (llq. ...
- Mybatis和spingboot整合
0. 导包 <!-- 统一管理springboot相关的包 --> <parent> <groupId>org.springframework.boot</g ...
- 07_Spring事务处理
一.事务概述 数据库的事务: 事务是一组操作的执行单元,相对于数据库操作来讲,事务管理的是一组SQL指令,比如增加,修改,删除等.事务的一致性,要求,这个事务内的操作必须全部执行成功,如果在此过程种出 ...
- NoSQL 列族数据库
- Luogu P1486 [NOI2004]郁闷的出纳员(平衡树)
P1486 [NOI2004]郁闷的出纳员 题意 题目描述 \(OIER\)公司是一家大型专业化软件公司,有着数以万计的员工.作为一名出纳员,我的任务之一便是统计每位员工的工资.这本来是一份不错的工作 ...