当前位置: 首页 > news >正文

P4198 楼房修建 详解

前言

同学们都是抄的这是真的吗
题解的做法真的一下子很难一步到位
非常难以理解 pushup 函数,这个函数也的确非常巧妙
所以作为一个个人做题记录的补充,这篇文章会比较详细的解释题目

如果有什么不太理解的东西可以留言

题目意思

要求维护一个可以单点修改,查询整个数组的字典序最小的最长上升子序列的数据结构

想法

首先单点修改就很像线段树,如果这个时候想到树状数组之类的东西也正常,因为单点修改仍然无法确定一个数据结构
但是看到了最长上升子序列发现这个东西是很难用普通的数据结构维护的,但是这个东西长得非常像最长上升子段,而子段是用线段树维护的,所以我们考虑使用线段树维护最长上升子序列

首先考虑最暴力的做法,就是直接记录他的最大值和区间的子序列长度,因为如果一个右区间它的最大值都小于左区间我们就没有必要去看右区间了,因为左区间的最大值一定会被取走,而右区间没有大于左区间的东西,也就是没有东西会取走,所以直接返回左区间的答案即可。
接下来考虑优化掉询问的时间复杂度,转化为修改的。

经过思索,我们发现我们只需要准备两个部分,一个修改,一个合并,建树部分只需要维护最长上升子序列长度和最大值即可,查询同理,而且查询永远是查询的最大的区间,所以不需要单独写一个函数

那么就开始一个一个考虑,先考虑修改函数
单点修改不会遗留 tag,所以不用 pushdown,所以只需要单点递归到我们需要的节点,考虑到我们要存一个答案,所以我们存下一个 \(siz\),表示这个区间的上升子序列长度,然后考虑怎么合并两个区间的答案

现在是合并函数
首先左区间的答案是必须要取走的,然后考虑右区间,因为我们右区间有且仅有一个上升子序列长度,而没有别的东西,更何况上升子序列无法 \(O(1)\) 合并,所以尝试再开一个函数 \(count{(i,j)}\) 表示 \(i\) 号节点要求上升子序列里的每一个数字都要 \(\ge j\),递归这个 \(count\) 该怎么求。
首先如果 \(i\) 是一个叶子节点,就看这个叶子结点的值大小是否 \(\ge j\) 即可,否则的话分类讨论,我们看他的左儿子和右儿子,记为 \(s_1,s_2\),如果 \(s_1\) 的最大值都要 \(\le j\) 说明左儿子取不走任何东西,那么只能递归右儿子,答案为 \(count{(s_2,j)}\),否则的话 \(s_1\) 的最大值一定会被取走,因为没有任何东西能挡住他,而 \(s_2\) 取走的东西一定要满足是上升的,所以 \(s_2\) 能取走的点一定是 \(\ge s_1\) 中最大值的点,而一开始我们计算的 \(x.siz\) 就是 \(s_1\) 取走的点加上 \(s_2\) 中满足条件能取走的点加和而成,所以 \(s_2\) 能取走的点不可能又不能取走了,所以 \(s_2\) 的贡献是 \(x.siz-s_1.siz\)\(s_1\) 的贡献是 \(count{(s_1,j)}\)
所以思路就出来了

思路

我们的线段树只用维护三个内容

  • 1.修改操作,这个部分我们单点修改即可,然后最后合并
  • 2.合并操作,这个部分的 \(siz\) 由左儿子和右儿子贡献,左儿子没有任何约束,所以是 \(s_1.siz\),而右儿子的约束是每个节点大小都要比做儿子最大的节点大,也就是 \(count{(s_2,s_1.mx)}\)
  • 3.\(count(x,p)\) 函数,表示 \(x\) 节点所有点都要 \(\ge p\) 的最长上升子序列,这个部分递归分讨,如果是叶子节点直接返回,否则的话看左儿子,如果左儿子最大值 \(\le j\) 说明没有利用价值了,直接递归右儿子,答案是 \(count{(s_2,j)}\),否则的话肯定会选走左儿子的最大值,那么对右儿子不会产生影响,左儿子的贡献是 \(count{(s_1,j)}\),右儿子的贡献是 \(x.siz-s_1.siz\),加和就是整个 \(count{(x,p)}\) 的答案。
    最后询问的时候直接输出线段树第一个结点的 \(siz\) 既可以了。

PS

题目给出的使每一个结点的高度,我们上文的所有计算都是使用的斜率,所以需要转化一下。

Code

int n,m;
struct tree{int lt,rt,siz;double mx;};
struct segment{#define lson(x) x<<1#define rson(x) x<<1|1tree tr[N<<2];inline void build(int id,int l,int r){tr[id].lt=l;tr[id].rt=r;if(l==r) return tr[id].siz=0,void();int mid=l+r>>1;build(lson(id),l,mid);build(rson(id),mid+1,r);tr[id].mx=max(tr[lson(id)].mx,tr[rson(id)].mx);}inline int count(int id,double h){if(tr[id].lt==tr[id].rt) return tr[id].mx>h;if(tr[lson(id)].mx<=h) return count(rson(id),h);else return tr[id].siz-tr[lson(id)].siz+count(lson(id),h);}inline void update(int id){tr[id].mx=max(tr[lson(id)].mx,tr[rson(id)].mx);tr[id].siz=tr[lson(id)].siz+count(rson(id),tr[lson(id)].mx);}inline void change(int id,int x,double y){if(tr[id].lt==tr[id].rt){tr[id].mx=y;tr[id].siz=1;return void();}int mid=tr[id].lt+tr[id].rt>>1;if(x<=mid) change(lson(id),x,y);else change(rson(id),x,y);update(id);}#undef lson#undef rson
}seg;int main(){cin>>n>>m;seg.build(1,1,n);for(int i=1;i<=m;i++){int x,y;cin>>x>>y;seg.change(1,x,double(y)*1.0/double(x));cout<<seg.tr[1].siz<<endl;}return 0;
}
http://www.sczhlp.com/news/10950/

相关文章:

  • linux 节约存储空间, 重复文件转换为硬链接
  • 短视频电商直播带货系统源码:核心功能与未来规划设计
  • python枚举enum
  • MyEMS 开源能源管理系统:重构能源秩序的技术密码
  • GreatSQL备份报错PROCESS权限不足分析与解决
  • LVM逻辑卷管理-磁盘分区与挂载
  • 端口镜像
  • 【Springer出版】第十届工程机械与车辆工程新进展国际学术会议(ICACMVE 2025)
  • 8/13
  • P2257题解.md
  • LTE网络中D2D通信模型及链路分析
  • python函数参数类型, 返回值类型, ...
  • 2025年最新Gitee本土化实践解析:代码托管平台如何赋能国内开发者
  • Dify搭建一个简单RAG知识库
  • CF2062H Galaxy Generator 题解
  • 流式改造优化阶段
  • 2025.7.30 CSP-S模拟赛30
  • 如何杜绝内外网文件交换中的病毒传播?这个检测机制必看
  • C语言总结_function
  • 类与对象 内存管理习题
  • gpgpu
  • SpringBoot3.5.4 整合Shiro时 运行失败,检查思路及最终解决方案
  • 优先级调度器和`时间轮`调度器
  • golang elastic search操作示例
  • xaml在线设计
  • 小白指南(三)——在Windows系统上安装minio存储系统
  • matlab实现利用双MZI产生RZ33-QPSK信号
  • 杂题做题日志
  • 自动驾驶 HIL 测试:构建 以假乱真 的实时数据注入系统
  • 牛 CDR3 单抗的开发难点与技术优化