单调栈
前置技能点:
- 栈
- 单调队列
闲话:
其实并不知道单调队列和单调栈的先后顺序,但既然我是先写单调队列的,那就把单调队列作为单调栈的前置技能点吧。
问题:
给定序列长度 n ,求每个点作为最大/最小值的最长区间。
思路:
对于每个点,可以直接向左向右枚举直到遇到比它小的数,复杂度为
对于每个点,可以二分以它作为左端点的区间的长度,然后用线段树得到它是不是该长度的区间的最小值,同理向右处理。复杂度建树 O(nlogn) ,对每个点的询问 O((logn)2) 。
对于每个点,用倍增的方法得到它右边第一个比它小的元素,同理向右处理。敲起来比线段树快,还能加个小优化。复杂度同线段树。
概念引入:
对于单调队列中循环删除队尾的情况,有这么一种性质(假设该单调队列单调递增):
下面说的左边和右边指的是原序列中的位置,左边表示从该元素开始向 id 较小的元素看,右边这是向 id 较大的元素看。
某个元素作为队尾被删除的条件:
它右边的第一个比它小的数字即将进入队列时。循环队尾删除操作的终止条件:
队列为空,或者队列中的队尾元素是即将压入元素左边的第一个比它小的元素。
所以可以取消掉单调队列的弹出队首操作,这样单调队列就是一个栈(其实觉得单调队列不算队列)了。
当有一个元素即将进栈时,需要先将栈内值大于它的函数全部弹栈,这个元素是被弹栈的元素右边第一个比它们小的元素,弹栈操作结束后,栈顶元素是该元素左边第一个比它小的元素。
若栈为空,则表示该元素是目前的最小值。
若整个序列进栈完毕,栈内剩余的每一个元素右边都没有比它小的元素。
复杂度:
预处理 O(n) ,询问 O(1) ,空间复杂度 O(n)
拓展
同单调队列