单调队列学习笔记(还是再回首)
单调队列的应用很常见,比如用于dp优化,以及滑动窗口问题等。
其思想也比较简单易懂。我们以求区间最大值的单调队列为例。
具体的来讲,我们让队头始终是最大的元素。为了保证数都在区间内,我们记录一个 \(t\) 数组。该数组表示某个元素加入的时间。我们每次将新元素(设为 \(x\))入队时,先将队头超时的元素弹出。然后,将队尾小于 \(x\) 的元素全部弹走。可以这样想: \(x\) 是新加入的元素,比队尾的元素时间要短;而 \(x\) 比这些元素大,因此,队尾小于 \(x\) 的元素不会对答案产生任何贡献。这样,我们就维护好一个单调队列了。
代码(这里最大值和最小值都求出来了):
#include<bits/stdc++.h>
using namespace std;
const int N = 1e6+100;
int lmx = 1, rmx, lmn = 1, rmn;
int tmx[N], tmn[N];
int qmx[N], qmn[N];
int a[N];
int n, K;
void push_mx(int x, int t){
while(tmx[lmx]<=t-K&&lmx<=rmx){
lmx++;
}
while(qmx[rmx]<=x&&lmx<=rmx){
rmx--;
}
qmx[++rmx] = x;
tmx[rmx] = t;
}
void push_mn(int x, int t){
while(tmn[lmn]<=t-K&&lmn<=rmn){
lmn++;
}
while(qmn[rmn]>=x&&lmn<=rmn){
rmn--;
}
qmn[++rmn] = x;
tmn[rmn] = t;
}
int main(){
scanf("%d%d", &n, &K);
for(int i = 1; i<= n; i++){
scanf("%d", &a[i]);
}
for(int i = 1; i<=n; i++){
push_mn(a[i], i);
if(i>=K){
printf("%d ", qmn[lmn]);
}
}
putchar('\n');
for(int i = 1; i<=n; i++){
push_mx(a[i], i);
if(i>=K){
printf("%d ", qmx[lmx]);
}
}
system("pause");
return 0;
}