单调栈是一种用来解决首递增序列问题的数据结构,其满足从栈顶元素到栈底元素单调的性质。单调栈还可以用来解决求矩形统计图中最大内矩形面积的问题,进一步可以用来求最小矩阵和问题。
对于序列 a_1,a_2,\cdots,a_N,定义从左往右 a_i的首递增序列为 a_{b_0}=a_i,a_{b_1},a_{b_2},\cdots,a_{b_m},满足 \forall j \in (0, m] ,都有 \forall 1 \leq k \lt b_j, a_{b_{j-1}} \geq a_k \wedge a_k \lt a_{b_j} 。
对于序列 a_1,a_2,\cdots,a_N,定义从左往右a_i 的首递减序列为从右往左a_i的首递增序列。
从栈顶元素到栈底元素单调递增。
从栈顶元素到栈底元素单调递减。
以求数组 a 中所有元素的首递减序列的长度的最大值为例。
以下图为例,求出矩形统计图最大内矩阵面积,易知最大面积为 8 。
而这个过程刚好符合单调递减栈的性质,于是乎就可以用单调递减栈来维护所有有效位置,处理完的无效位置就被弹出栈了。即:
【注】以下模板仅给出最原始的单调栈结构,可以用来解决简单的首递增问题。对于进一步解决矩形统计图最大内矩形面积的问题,需要在弹栈入栈时做额外的操作,关于具体的应用可以参考 P4147「玉蟾宫」。
#include <bits/stdc++.h>
using namespace std;
#ifndef _MSTACK_
#define _MSTACK_
#define ll int
#define MAXN 100005
// 比较元素
template < typename T >
struct Compare {
bool operator () (const T & a, const T & b) {
return a < b; // 从左往右首递增序列(单调递减栈)
return a > b; // 从左往右首递减序列(单调递增栈)
}
};
// 单调栈
template < typename T, typename F = Compare<T> >
struct MStack{
ll depth; // 栈深
T stack[MAXN]; // 单增栈
F cmp;
MStack():depth(0) {}
// 二分查找
ll find(T x, ll l, ll r) {
if(l == r) return l;
ll m = (l+r) >> 1;
if(cmp(stack[m], x)) {
return find(x, m+1, r);
} else {
return find(x, l, m);
}
}
// 压栈
T push(T x) {
// // 方法一
// // 顺序弹栈 O(n),整体 O(2n)
// while(depth && !cmp(stack[depth-1], x)) --depth;
// stack[depth++] = x;
// return x;
// 方法二
// 二分查找 O(lg(n)),整体 O(nlg(n))
if(!depth || cmp(stack[depth-1], x)) {
stack[depth++] = x;
return x;
}
ll pos = find(x, 0, depth-1);
T t = stack[pos];
stack[pos] = x;
depth = pos + 1;
return t;
}
// 弹栈
T pop() {
return stack[--depth];
}
// 清空栈
void clear() {
depth = 0;
}
};
#endif