“我永远对你保有最诚挚的热情。”
单调队列
单调队列是一种队列内的元素有单调性(递增或递减)的队列。答案(最优解)存在队首。一般用于维护区间最值或者降低 $Dp$ 数组的维度来减少空间及时间的目的。
单调队列的作用
1. 维护区间最值2. 优化 DP
单调队列在队首和队尾都可以进行出队操作,但只有队尾可以进行入队操作。这样的操作类似于双端队列($deque$)允许双端弹出。但一般来说,还是不建议使用 $STL$ 库,手写方便且比较节省时间。
总的来说,单调队列的实现有三步:
- 将 $head$ 之前所有已在区间之外的答案删掉( $++head$ )
- 更新答案为 $Queue[head]$
- 将尾部的非最优答案排除( $—tail$ ),并入队最新答案。
栗子
题意
求出区间最大值和最小值,因为复杂度限制 $O(n \log n)$ 所以无法暴力。就使用单调队列的优化。
AC Code:
查看代码
1 |
|
单调队列优化多重背包
查看代码
1 |
|
提升
解释
二维的滑动窗口,也就那样做就对了。
AC Code
查看代码
1 |
|
练习
单调栈
同样的,表示一个栈 $S$ 中的元素满足单调递增或者单调递减的性质,可以用来维护 RMQ
问题。
栈是一种先进后出、后进先出的数据结构,栈和队列应该是最简单的两种数据结构了,其原理与实现非常简单。单调栈中的元素是严格单调递增或者递减的,也就是说:从栈底到栈顶,元素的值逐渐增大或者减小。虽然单调栈的性质很简单,但是其用处很大,可以用于求解元素的左右大小边界问题。
例题
对于查询后继的题,具有后效性,对我而言不太好处理;所以我们考虑倒序枚举。将问题转化为查找前继中第一个比该数大的下标,以样例为例:
1 | 1 4 2 3 5 |
建立一个单调递减的栈 $S$ ,接下来我们来手动模拟:
1 | 输入 5 |
一些小问题:
- 关于栈空的处理,如果是使用 $STL$ 的话,要先判是否为空,否则会 $RE$ 。且对于栈空的情况,有些题目是需要特判的。
- 关于取不取等的情况,视题目而定,就这道题而言,因为我们查找的是最近的一个,所以对于有 $a_i=a_j,i<j$ 的话,肯定有 $i$ 答案是更优的,所以在此题,相等情况下是需要出栈的。
AC Code
1 |
|
巩固
考虑枚举右端点 $B$。根据题意,因为左端点 $A$ 一定是最矮的,所以 $A$ 一定是当前序列(从 $1$ 到 $B$)的后缀最小值所在的位置。
因为 $B$ 一定是最高的,所以 $A$ 到 $B$ 之间不能有任何元素比 $B$ 高,因此 $A$ 的右侧一定只有 $B$ 一个位置可以作为当前序列的后缀最大值。换句话说,我们要找到从后向前数第二个后缀最大值的位置 $k$,$A$ 一定在该位置的右侧。并且只要 $A$ 在 $k$ 右侧且 $A$ 是当前序列的后缀最小值,那么 $A$ 就是一个合法的左端点。
考虑用单调栈来维护当前序列的后缀最大最小值,每次新枚举到一个 $B$ 时,先不将新位置入栈,此时的最大值栈顶就是第二个后缀最大值的位置。而维护后缀最小值的单调栈中的下标也是单调的,因此直接在最小值栈上二分即可找到最靠左的对应 $A$ 的位置。更新答案即可。时间复杂度 $\mathcal O(n \log n)$。
AC Code
1 |
|
维护比第 $x$ 个数大的偏左和偏右下标,每一个点贡献的答案就是:
$ans=\max\limits_{1\le i\le n}\{x_i\times(s_{right_i}-s_{left_i-1})\}$
从前往后,从后往前各一次单调栈后统计答案,应该要开 long long
,没试过。
AC Code
1 |
|
—