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 然后说下,这题有很多种方法, ...
随机推荐
- BZOJ 1579 [Usaco2009 Feb]Revamping Trails 道路升级
堆优化的dijkstra. 把一个点拆成k个. 日常空间要开炸一次.. //Twenty #include<cstdio> #include<cstring> #include ...
- C# StructLayout(LayoutKind.Sequential)]
结构体是由若干成员组成的.布局有两种1.Sequential,顺序布局,比如struct S1{ int a; int b;}那么默认情况下在内存里是先排a,再排b也就是如果能取到a的地址,和b的 ...
- 深入浅出 Java Concurrency (24): 并发容器 part 9 双向队列集合 Deque[转]
有一段时间没有更新了.接着上节继续吧. Queue除了前面介绍的实现外,还有一种双向的Queue实现Deque.这种队列允许在队列头和尾部进行入队出队操作,因此在功能上比Queue显然要更复杂.下图描 ...
- 杂项-SpringBoot-Jasypt:Jasypt(安全框架)
ylbtech-杂项-SpringBoot-Jasypt:Jasypt(安全框架) 1. 使用jasypt加密Spring Boot应用中的敏感配置返回顶部 1. 本文讲述了在Spring Boot/ ...
- 转:Wireshark基本介绍和学习TCP三次握手
源地址:http://www.cnblogs.com/TankXiao/archive/2012/10/10/2711777.html 之前写过一篇博客:用 Fiddler 来调试HTTP,HTTPS ...
- windows环境下运行Elasticsearch
1.Elasticsearch下载地址:https://github.com/medcl/elasticsearch-rtf 直接下载ZIP包: 2.配置JAVA环境 jdk64位地址:jdk-win ...
- 2019牛客暑期多校赛(第三场)B-求01串中的最长01数量相等的子串和子序列
https://ac.nowcoder.com/acm/contest/883/B 首先先把0所在的位置变-1,1所在位置变1,然后统计一个前缀和,用sum[i]表示. 那么如果从起点开始的话只要满足 ...
- java笔试之计算n x m的棋盘格子
请编写一个函数(允许增加子函数),计算n x m的棋盘格子(n为横向的格子数,m为竖向的格子数)沿着各自边缘线从左上角走到右下角,总共有多少种走法,要求不能走回头路,即:只能往右和往下走,不能往左和往 ...
- Swoole协程报错 Uncaught Error: Call to undefined function go()
解决方法, 在PHP.ini中开启短名
- 关于js的闭包和复制对象
一.有关js的闭包 1.概念:所谓的闭包,就是指的两个作用域,其中内层作用于可以访问外层作用域的函数的现象 2.简单应用 for(var i = 0;i< lis.lenth;i++){ (fu ...