单调队列学习笔记(还是再回首)

单调队列的应用很常见,比如用于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;
}
posted @ 2023-06-07 20:57  霜木_Atomic  阅读(19)  评论(0编辑  收藏  举报