单调栈和单调队列
RMQ问题
问题引入:RMQ(x, y)就是询问数组【x, y】区间内部的最小值
如:RMQ(0, 3) = 1, RMQ(3, 7) = 2
数组arr【3, 1, 4 ,5 ,2, 9, 8, 12】;
分别指的是0 - 3下标中的最小值为 1, 3 - 7下标中的最小值为2
前缀和序列
设存在原序列,且
a
0
=
0
a_0 = 0
a0=0:
a
0
,
a
1
,
a
2
,
a
3
,
.
.
.
,
a
n
−
1
,
a
n
;
(1.1)
a_0, a_1, a_2, a_3, ..., a_{n - 1}, a_n;\tag{1.1}
a0,a1,a2,a3,...,an−1,an;(1.1)
所谓前缀和序列指的是,前N项和等于前N - 1项和加上第N项的序列:
S
n
=
S
n
−
1
+
a
n
;
(1.2)
S_n = S_{n - 1} + a_n; \tag{1.2}
Sn=Sn−1+an;(1.2)
即前缀和序列为:
S
1
,
S
2
,
S
3
,
.
.
.
,
S
n
−
2
+
a
n
−
1
,
S
n
−
1
+
a
n
;
(1.3)
{S_1, S_2, S_3,..., S_{n - 2} + a_{n - 1}, S_{n - 1} + a_n};\tag{1.3}
S1,S2,S3,...,Sn−2+an−1,Sn−1+an;(1.3)
反过来也可以通过前缀和序列(3)求得原序列第N项的值:
a
n
=
S
n
−
S
n
−
1
;
(1.4)
a_n = S_n - S_{n - 1};\tag{1.4}
an=Sn−Sn−1;(1.4)
即由(4)可得到:
a
0
,
a
1
,
a
2
,
a
3
,
.
.
.
,
S
n
−
1
−
S
n
−
2
,
S
n
−
S
n
−
1
等价于
(
1.1
)
;
(1.5)
a_0, a_1, a_2, a_3, ..., S_{n-1} - S_{n-2}, S_n - S_{n - 1} 等价于(1.1);\tag{1.5}
a0,a1,a2,a3,...,Sn−1−Sn−2,Sn−Sn−1等价于(1.1);(1.5)
差分序列
差分序列指的是,第N项与第N - 1项差的序列(设
a
0
=
0
a_0 = 0
a0=0):
X
n
=
a
n
−
a
n
−
1
;
(2.1)
X_n = a_n - a_{n - 1};\tag{2.1}
Xn=an−an−1;(2.1)
即:
X
1
,
X
2
,
X
3
,
.
.
.
X
n
−
1
,
X
n
;
(2.2)
X_1, X_2, X_3, ...X_{n-1}, X_n;\tag{2.2}
X1,X2,X3,...Xn−1,Xn;(2.2)
单调队列
维护一个以j位置为结尾的单调序列,就可以维护(i, j)的最值答案, 加上区间长度的限制,就是所谓的单调队列;
单调队列与普通队列的不同就在于为了维护队列的单调性,在新元素入队前会先判断队尾元素和待入队元素的大小关系是否与队列的单调性一致;一致就入队,否则从队尾淘汰老元素;同时如果队首元素超出了区间长度的范围,就会在队首执行出队操作;
单调栈
相对于单调队列而言,单调栈不需要进行过期判断,即队首元素出队操作,因此单调栈所维护的性质就是最近最值(大于|小于关系)问题;
练习
单调队列
#include<stdio.h>
#define MAX_N 300000
int arr[MAX_N + 5];
int que[MAX_N + 5];
int main() {
int N, K;
scanf("%d%d", &N, &K);
for (int i = 1; i <= N; i++) scanf("%d", &arr[i]);
//求最小值
int head = 0, tail = 0;
for (int i = 1; i <= N; i++) {
while (tail - head && arr[i] < arr[que[tail - 1]]) tail--;
que[tail++] = i;
if (i < K) continue;
i != K && printf(" ");
if (i - que[head] >= K) head++;
printf("%d", arr[que[head]]);
}
printf("\n");
//求最大值
head = 0, tail = 0;
for (int i = 1; i <= N; i++) {
while (tail - head && arr[i] > arr[que[tail - 1]]) tail--;
que[tail++] = i;
if (i < K) continue;
i != K && printf(" ");
if (i - que[head] >= K) head++;
printf("%d", arr[que[head]]);
}
printf("\n");
return 0;
}
#include<stdio.h>
#include<inttypes.h>
#define MAX_N 300000
int S[MAX_N + 5];
int que[MAX_N + 5], head = 0, tail = 0;
int ans = INT32_MIN;
int main() {
int N, M;
scanf("%d%d", &N, &M);
for (int i = 1; i <= N; i++ ) {
scanf("%d", &S[i]);
S[i] += S[i - 1];
}
que[tail++] = 0;
for (int i = 1; i <= N; i++) {
if (i - que[head] > M) head++;
ans = ans >= S[i] - S[que[head]] ? ans: S[i] - S[que[head]];
while (tail - head && S[i] <= S[que[tail - 1]]) tail--;
que[tail++] = i;
}
printf("%d\n", ans);
return 0;
}
单调栈
#include<stdio.h>
#define MAX_N 100000
long long l[MAX_N + 5];
long long r[MAX_N + 5], stk[MAX_N + 5];
long long S[MAX_N + 5], ans = 0;
int main() {
int N;
scanf("%d", &N);
for (int i = 1; i <= N; i++) {
scanf("%lld", &S[i]);
}
l[0] = -1, l[N + 1] = -1;
r[0] = -1, r[N + 1] = -1;
int top = 0;
stk[top = 0] = 0;
//从左往右
for (int i = 1; i <= N; i++) {
while (S[stk[top]] >= S[i]) top--;
l[i] = stk[top];
stk[++top] = i;
}
stk[top = 0] = 0;
//从右往左
for (int i = N; i >= 1; i--) {
while (S[stk[top]] >= S[i]) top--;
r[i] = stk[top];
stk[++top] = i;
}
//计算
for (int i = 1; i <= N; i++) {
ans = ans >= S[i] * (r[i] - l[i] - 1) ? ans : S[i] * (r[i] - l[i] - 1);
}
printf("%lld\n", ans);
return 0;
}
#include<stdio.h>
#define MAX_N 500000
int N, P = 1;
int a[MAX_N + 5], b[MAX_N + 5];
int main() {
scanf("%d", &N);
for (int i = 0; i < N; i++) {
scanf("%d", &a[i]);
}
for (int i = 0; i < N; i++) {
scanf("%d", &b[i]);
}
int top1 = -1, top2 = -1;
while (P < N) {
while (top1 != -1 && a[top1] >= a[P]) top1--;
while (top2 != -1 && b[top2] >= b[P]) top2--;
if (top1 - top2) break;
a[++top1] = a[P];
b[++top2] = b[P];
P++;
}
printf("%d\n", P);
return 0;
}