【笔记】线段维护单调栈

【笔记】线段维护单调栈

维护单调栈的信息可以包括:

  1. 单调栈的大小;
  2. 单调栈里面元素的权值(可以和键值不一样)的极值/和。

1 核心思想

(以维护严格单增的单调栈为例,单减同理。

首先线段树内会维护区间最大值 mx,和区间的答案 ans

定义一个函数:calc(p,x) 表示在线段树的 p 节点,删去区间内小于等于 x 的元素,剩下构成的答案。

那么 ans(p)=ans(ls)+calc(rs,mx(ls))。(这个 + 是合并的意思,不是真正意义上的加法。

考虑怎么算 calc(p,x)

分讨一下:

  1. 区间长度为 1:略;

  2. 如果 mx(ls)<xcalc(p,x)=calc(rs,x)

  3. 如果 mx(ls)xcalc(p,x)=calc(ls,x)+calc(rs,mx(ls))

    这样乍看上去好像并没有什么用,还是 O(n) 的,但实际上变化就在于后面是 calc(rs,mx(ls)),而不是 calc(rs,x)

    相对于 xmx(ls) 可以视为定值,因为我们每次更新 mx 的时候,就可以同步算出新的 calc(rs,mx(ls)),然后储存到一个变量里面。

这样的话,我们就保证了 calc(p,x),可以在 O(logn) 的时间复杂度内求解。

而每次 push_up 的时候都要调用一遍,所以总的时间复杂度就是 O(nlog2n)


2 例题

2.1 楼房重建

因为固定了在 (0,0) 看,所以不完全是板子,比如 (1,2) (2,3) 答案就应该是 1 而不是 2

其实仔细想一下,我们并不是要求房子的高度单增,而是房子的斜率单增,然后就是板子了。

维护单调栈长度的板子。


2.2 「JOISC 2014 Day3」稻草人

注意一件事情,理论上,我们从前往后/从后往前做、单增/单减都是可以任意选取的。

但是如果 calc 算的是小于等于某个值构成的单调栈的话,就不能同时求出大于等于某个值的单调栈(?

为了规避掉这个问题,我们直接按照 y 从小到大加入,维护从右往左做单增的单调栈,每次查询前缀。


posted @   CloudWings  阅读(53)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 一文读懂知识蒸馏
· 终于写完轮子一部分:tcp代理 了,记录一下
点击右上角即可分享
微信分享提示