【笔记】线段维护单调栈
【笔记】线段维护单调栈
维护单调栈的信息可以包括:
- 单调栈的大小;
- 单调栈里面元素的权值(可以和键值不一样)的极值/和。
1 核心思想
(以维护严格单增的单调栈为例,单减同理。
首先线段树内会维护区间最大值 \(mx\),和区间的答案 \(ans\)。
定义一个函数:\(calc(p,x)\) 表示在线段树的 \(p\) 节点,删去区间内小于等于 \(x\) 的元素,剩下构成的答案。
那么 \(ans(p)=ans(ls)+calc(rs,mx(ls))\)。(这个 \(+\) 是合并的意思,不是真正意义上的加法。
考虑怎么算 \(calc(p,x)\)。
分讨一下:
-
区间长度为 \(1\):略;
-
如果 \(mx(ls)<x\):\(calc(p,x)=calc(rs,x)\);
-
如果 \(mx(ls)\ge x\):\(calc(p,x)=calc(ls,x)+calc(rs,mx(ls))\)
这样乍看上去好像并没有什么用,还是 \(O(n)\) 的,但实际上变化就在于后面是 \(calc(rs,mx(ls))\),而不是 \(calc(rs,x)\)。
相对于 \(x\),\(mx(ls)\) 可以视为定值,因为我们每次更新 \(mx\) 的时候,就可以同步算出新的 \(calc(rs,mx(ls))\),然后储存到一个变量里面。
这样的话,我们就保证了 \(calc(p,x)\),可以在 \(O(\log n)\) 的时间复杂度内求解。
而每次 push_up
的时候都要调用一遍,所以总的时间复杂度就是 \(O(n\log^2 n)\)。
2 例题
2.1 楼房重建
因为固定了在 \((0,0)\) 看,所以不完全是板子,比如 \((1,2)~(2,3)\) 答案就应该是 \(1\) 而不是 \(2\)。
其实仔细想一下,我们并不是要求房子的高度单增,而是房子的斜率单增,然后就是板子了。
维护单调栈长度的板子。
2.2 「JOISC 2014 Day3」稻草人
注意一件事情,理论上,我们从前往后/从后往前做、单增/单减都是可以任意选取的。
但是如果 calc
算的是小于等于某个值构成的单调栈的话,就不能同时求出大于等于某个值的单调栈(?
为了规避掉这个问题,我们直接按照 \(y\) 从小到大加入,维护从右往左做单增的单调栈,每次查询前缀。