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 然后说下,这题有很多种方法, ...
随机推荐
- bootStrap-treeview插件
简要教程 bootstrap-treeview是一款效果非常酷的基于bootstrap的jQuery多级列表树插件.该jQuery插件基于Twitter Bootstrap,以简单和优雅的方式来显示一 ...
- 安装Ubuntu16.04卡在logo界面
问题背景 笔者在使用U盘UEFI模式安装Ubuntu16.04时,遇到一个问题,即在BIOS里的boot设置U盘为第一启动项之后,启动,并没有顺利进入系统,而是卡在了logo界面.(PS:其实我等了它 ...
- 洛谷P3694 邦邦的大合唱
题目背景 BanG Dream!里的所有偶像乐队要一起大合唱,不过在排队上出了一些问题. 题目描述 N个偶像排成一列,他们来自M个不同的乐队.每个团队至少有一个偶像. 现在要求重新安排队列,使来自同一 ...
- 02.vs插件 获取项目和解决方案路径
获取项目 解决方案路径 /// <summary> /// 获取并设置项目和解决方案绝对路径 /// </summary> /// <returns></re ...
- 深入理解JVM(一)类加载器部分:双亲委派模型
类加载器的父亲委托机制 在父亲委托机制中,各个类加载器按照父子关系形成了树形结构,除了根类加载器之外,其余的类加载器都有且只有一个父加载器. 先让最顶层可以加在的父加载器加栽(所有可加载的加载器中,处 ...
- ie浏览器下载附件中文乱码
String llq = request.getHeader( "USER-AGENT" ).toLowerCase();Boolean isIE = false;if (llq. ...
- Python GUI文本编辑器
使用Python编写一个简单的文本编辑器,需要展示一个用户界面,功能包括打开.保存文本文件. 使用tkinter库来编写GUI. #简单GUI文本编辑器 from tkinter import * f ...
- C语言开发系列-二进制
n位二进制的取值范围 -2的n-1次方 ~ 2的n-1次方-1 输出一个整数的二进制的存储形式 #include <stdio.h> // 输出一个整数的二进制的存储形式 void put ...
- Java基础知识(多线程和线程池)
新建状态: 一个新产生的线程从新状态开始了它的生命周期.它保持这个状态直到程序 start 这个线程. 运行状态:当一个新状态的线程被 start 以后,线程就变成可运行状态,一个线程在此状态下被认为 ...
- agc034
A:题意:你有一个1 * n的网格,有些地方是障碍.你有两个人,分别要从a到b和从c到d,一次只能向右跳1步或者两步.求是否可行. 解:先判断有没有2个连续的障碍,然后判断是否能错车. #includ ...