forked from EndlessCheng/codeforces-go
-
Notifications
You must be signed in to change notification settings - Fork 0
/
monotone_stack.go
216 lines (203 loc) · 6.73 KB
/
monotone_stack.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
package copypasta
/* 单调栈 Monotone Stack
举例:返回每个元素两侧严格大于它的元素位置(不存在则为 -1 或 n)
如何理解:把数组想象成一列山峰,站在 a[i] 的山顶仰望两侧的山峰,是看不到高山背后的矮山的,只能看到一座座更高的山峰
这就启发我们引入一个底大顶小的单调栈,入栈时不断比较栈顶元素直到找到一个比当前元素大的
技巧:事先压入一个边界元素到栈底,这样保证循环时栈一定不会为空,从而简化逻辑
一些转换:
若区间 [l,r] 的最大值等于 a[r],则 l 必须 > posL[r]
若区间 [l,r] 的最大值等于 a[l],则 r 必须 < posR[l]
这一结论可以用于思考一些双变量的题目
https://oi-wiki.org/ds/monotonous-stack/
https://cp-algorithms.com/data_structures/stack_queue_modification.html
模板题
https://www.luogu.com.cn/problem/P5788
https://www.luogu.com.cn/problem/P2866 http://poj.org/problem?id=3250
https://leetcode-cn.com/problems/next-greater-element-i/ LC496/周赛18BA
https://leetcode-cn.com/problems/next-greater-element-ii/ LC503/周赛18BB
NEERC05,UVa 1619 https://onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&category=825&page=show_problem&problem=4494
与 DP 结合
https://codeforces.com/problemset/problem/1313/C2
https://codeforces.com/problemset/problem/1407/D
结合线段树,或者巧妙地在单调栈中去维护最值 https://codeforces.com/problemset/problem/1483/C
单调队列优化 LC375 猜数字大小 II https://leetcode-cn.com/problems/guess-number-higher-or-lower-ii/
LC42 接雨水 https://leetcode-cn.com/problems/trapping-rain-water/
评注:接雨水有三种不同的解法(DP、单调栈和双指针),其中双指针是 DP 的空间优化写法
本质上是两种计算策略:计算每个下标处的接水量(纵向累加),计算一段高度对应的接水宽度(横向累加)
LC84 柱状图中最大的矩形 https://leetcode-cn.com/problems/largest-rectangle-in-histogram/ http://poj.org/problem?id=2559 http://poj.org/problem?id=2082
LC85 最大全 1 矩形(实现见下面的 maximalRectangleArea)https://leetcode-cn.com/problems/maximal-rectangle/ 原题为 http://poj.org/problem?id=3494
LC1504/周赛196C 全 1 矩形个数(实现见下面的 numSubmat)https://leetcode-cn.com/problems/count-submatrices-with-all-ones/
后缀数组+不同矩形对应方案数之和 https://codeforces.com/edu/course/2/lesson/2/5/practice/contest/269656/problem/D
与 bitOpTrickCnt 结合(见 bits.go)https://codeforces.com/problemset/problem/875/D
已知部分 posR 还原全部 posR;已知 posR 还原 a https://codeforces.com/problemset/problem/1158/C
*/
func monotoneStack(a []int) ([]int, []int) {
const border int = -2e9 // 求两侧大的话用 2e9
type pair struct{ v, i int }
// 求左侧严格小于
n := len(a)
posL := make([]int, n)
stack := []pair{{border, -1}}
for i, v := range a {
for {
if top := stack[len(stack)-1]; top.v < v { //
posL[i] = top.i
break
}
stack = stack[:len(stack)-1]
}
stack = append(stack, pair{v, i})
}
// 求右侧严格小于
posR := make([]int, n)
stack = []pair{{border, n}}
for i := n - 1; i >= 0; i-- {
v := a[i]
for {
if top := stack[len(stack)-1]; top.v < v { //
posR[i] = top.i
break
}
stack = stack[:len(stack)-1]
}
stack = append(stack, pair{v, i})
}
// EXTRA
mx := 0
for i, v := range a {
l, r := posL[i]+1, posR[i] // [l,r)
if v *= r - l; v > mx {
mx = v
}
}
// EXTRA: 求所有长为 i 的子区间的最小值的最大值
// https://codeforces.com/problemset/problem/547/B LC1950 https://leetcode-cn.com/problems/maximum-of-minimum-values-in-all-subarrays/
{
ans := make([]int, n+1)
for i := range ans {
ans[i] = -2e9
}
for i, v := range a {
sz := posR[i] - posL[i] - 1
if v > ans[sz] {
ans[sz] = v
}
}
for i := n - 1; i > 0; i-- {
if ans[i+1] > ans[i] {
ans[i] = ans[i+1]
}
}
// ans[1:]
}
return posL, posR
}
// 注:若输入的是一个 1~n 的排列,有更简单的写法(求两侧大于位置)
// 为简单起见,求出的下标从 1 开始(不存在时表示为 0 或 n+1)
// https://codeforces.com/contest/1156/problem/E
func permPosLR(a []int) ([]int, []int) {
n := len(a)
posV := make([]int, n+1)
posL := make([]int, n+2)
posR := make([]int, n+1)
for i := 1; i <= n; i++ {
posV[a[i-1]] = i
posL[i], posR[i] = i-1, i+1
}
// 正序遍历求出的是两侧大于位置
// 倒序遍历求出的是两侧小于位置
for v := 1; v <= n; v++ {
p := posV[v]
posR[posL[p]] = posR[p]
posL[posR[p]] = posL[p]
}
return posL, posR
}
// 最大全 1 矩形
// LC85 https://leetcode-cn.com/problems/maximal-rectangle/
func maximalRectangleArea(mat [][]int) (ans int) {
const target = 1
n, m := len(mat), len(mat[0])
heights := make([][]int, n) // heights[i][j] 表示从 (i,j) 往上看的高度(连续 1 的长度),mat[i][j] = 0 时为 0
for i, row := range mat {
heights[i] = make([]int, m)
for j, v := range row {
if v == target {
if i == 0 {
heights[i][j] = 1
} else {
heights[i][j] = heights[i-1][j] + 1
}
}
}
}
// 然后枚举每一行,就变成 LC84 这题了
type pair struct{ h, i int }
for _, hs := range heights {
posL := make([]int, m)
stack := []pair{{-1, -1}}
for j, h := range hs {
for {
if top := stack[len(stack)-1]; top.h < h {
posL[j] = top.i
break
}
stack = stack[:len(stack)-1]
}
stack = append(stack, pair{h, j})
}
posR := make([]int, m)
stack = []pair{{-1, m}}
for j := m - 1; j >= 0; j-- {
h := hs[j]
for {
if top := stack[len(stack)-1]; top.h < h {
posR[j] = top.i
break
}
stack = stack[:len(stack)-1]
}
stack = append(stack, pair{h, j})
}
for j, h := range hs {
if area := (posR[j] - posL[j] - 1) * h; area > ans {
ans = area
}
}
}
return
}
// 全 1 矩形个数
// LC1504/周赛196C https://leetcode-cn.com/problems/count-submatrices-with-all-ones/
// 参考 https://leetcode.com/problems/count-submatrices-with-all-ones/discuss/720265/Java-Detailed-Explanation-From-O(MNM)-to-O(MN)-by-using-Stack
func numSubmat(mat [][]int) (ans int) {
m := len(mat[0])
heights := make([]int, m)
for _, row := range mat {
sum := make([]int, m)
type pair struct{ h, j int }
stack := []pair{{-1, -1}}
for j, v := range row {
if v == 0 {
heights[j] = 0
} else {
heights[j]++
}
h := heights[j]
for {
if top := stack[len(stack)-1]; top.h < h {
if pre := top.j; pre < 0 {
sum[j] = (j + 1) * h
} else {
sum[j] = sum[pre] + (j-pre)*h
}
ans += sum[j]
break
}
stack = stack[:len(stack)-1]
}
stack = append(stack, pair{h, j})
}
}
return
}