好,长草博客被催更了【?】

我感觉这题完全可以当作线段树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监控【线段树】的更多相关文章

  1. 洛谷题解P4314CPU监控--线段树

    题目链接 https://www.luogu.org/problemnew/show/P4314 https://www.lydsy.com/JudgeOnline/problem.php?id=30 ...

  2. 【bzoj3064】Tyvj 1518 CPU监控 线段树维护历史最值

    题目描述 给你一个序列,支持4种操作:1.查询区间最大值:2.查询区间历史最大值:3.区间加:4.区间赋值. 输入 第一行一个正整数T,表示Bob需要监视CPU的总时间. 然后第二行给出T个数表示在你 ...

  3. 【BZOJ】1012: [JSOI2008]最大数maxnumber /【洛谷】1198(线段树)

    Description 现在请求你维护一个数列,要求提供以下两种操作:1. 查询操作.语法:Q L 功能:查询当前数列中末尾L个数中的最大的数,并输出这个数的值.限制:L不超过当前数列的长度.2. 插 ...

  4. 洛谷P3372/poj3468(线段树lazy_tag)(询问区间和,支持区间修改)

    洛谷P3372 //线段树 询问区间和,支持区间修改 #include <cstdio> using namespace std; struct treetype { int l,r; l ...

  5. CPU监控 线段树裸题

    LINK:bzoj3064 此题甚好码了20min停下来思考的时候才发现不对的地方有点坑... 还真不好写来着 可这的确是线段树的裸题...我觉得我写应该没有什么大问题 不过思路非常的紊乱 如果是自己 ...

  6. 洛谷P4065 [JXOI2017]颜色(线段树)

    题意 题目链接 Sol 线段树板子题都做不出来,真是越来越菜了.. 根据题目描述,一个合法区间等价于在区间内的颜色没有在区间外出现过. 所以我们可以对于每个右端点,统计最长的左端点在哪里,刚开始以为这 ...

  7. 洛谷P5111 zhtobu3232的线段树

    题意:给定线段树,上面若干个节点坏了,求能表示出多少区间. 区间能被表示出当且仅当拆出来的log个节点都是好的. 解:每个区间在最浅的节点处计算答案. 对于每个节点维护从左边过来能有多少区间,从右边过 ...

  8. BZOJ.3064.CPU监控(线段树 历史最值)

    题目链接 \(Description\) 有一个长为n的序列Ai,要求支持查询[l,r]的最值.历史最值,区间加/重设 \(Solution\) 线段树,每个点再维护一个历史(从0到现在)最大值.历史 ...

  9. 洛谷P3960 列队 NOIp2017 线段树/树状数组/splay

    正解:动态开点线段树 解题报告: 传送门! 因为最近学主席树的时候顺便get到了动态开点线段树?刚好想起来很久很久以前就想做结果一直麻油做的这题,,,所以就做下好了QAQ 然后说下,这题有很多种方法, ...

随机推荐

  1. php计算网段内所有IP,分配子网段

    由于最近业务需要,写了个获取网段内所有IP的函数,以及分配可用子网段的函数 /** * 根据网段获取计算所有IP * @param string $segment 网段 '139.217.0.1/24 ...

  2. VS2010-MFC(对话框:模态对话框及其弹出过程)

    转自:http://www.jizhuomi.com/software/160.html 一.模态对话框和非模态对话框 Windows对话框分为两类:模态对话框和非模态对话框. 模态对话框是这样的对话 ...

  3. jeecms v9库内新增对象的流程及其他技巧

    cms 开发 ———— 库内新增对象 Products 的流程说明及其他技巧 第一步:Entity com.jeecms.cms.entity.assist.base下建立模型基础类BaseCmsPr ...

  4. 转:linux下的c/c++开发

    源地址:http://zhidao.baidu.com/question/131261452.html 我就是做LINUX下的C开发的. 准确的说,LINUX下C才是主要的开发语言,但是写应用程序还是 ...

  5. 前端面试题(js部分)

    一.==和===的区别 1.==   用于比较.判断两者相等,比较时可自动换数据类型 2.===  用于(严格)比较.判断两者(严格)相等,不会进行自动转换,要求进行比较的操作数必须类型一致,不一致时 ...

  6. kafka offset manage

    kafka low api:fetch数据从topic partition offset buffsize长度. 提交一般两个维度:时间维度,满多少条提交(0.8X之前是没这参数) 在0.8.2.2版 ...

  7. Redis安装过程:

  8. 网络结构解读之inception系列四:Inception V3

    网络结构解读之inception系列四:Inception V3   Inception V3根据前面两篇结构的经验和新设计的结构的实验,总结了一套可借鉴的网络结构设计的原则.理解这些原则的背后隐藏的 ...

  9. Leetcode145. Binary Tree Postorder Traversal二叉树的后序遍历

    给定一个二叉树,返回它的 后序 遍历. 进阶: 递归算法很简单,你可以通过迭代算法完成吗? 递归: class Solution { public: vector<int> res; ve ...

  10. anime.js 学习笔记

    官网演示/文档 anime.js 是一个简便的JS动画库,用法简单而且适用范围广,涵盖CSS,DOM,SVG还有JS的对象,各种带数值属性的东西都可以动起来. 实际演示和代码,官网写得很详细清楚了,这 ...