单调队列模板题。
单调队列有两个性质:
1.数值大小单调 2.队列元素位置单调
插入查询的规则是,如果要求最小值,那么队列要从小到大排序,结果在队头。求最大值则相反。这是因为插入时要满足性质2,所以只能插在队尾,那么新元素要替换的值应该是比它大的,所以队列整体要从小到大排,才能保证大的数都在队尾一侧,实现查询最小值。
插入时替换比它大的数的理由是,对于新的数,因为位置大于老的数,从这个位置开始所有包含老的数的区间都包含新的数。如果新的数小于老的数,那么查询到的最小值永远不会是老的那个数,可以直接把他出队。
本题因为还有一个区间的限制,所以要用一个结构体记录每个元素的位置,并要用队头队尾两个指针。如果队头元素位置已经超出区间,队头要移动。
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
struct node{
int pos,v;
};
node a[1000005],q[1000005];
int hd,tl;
int N,K;
int main(){
scanf("%d%d",&N,&K);
for(int i=0;i<N;i++){
scanf("%d",&a[i].v);
a[i].pos=i;
}
hd=tl=0;
q[0]=a[0];
for(int i=1;i<K-1;i++){
while(tl>=hd&&q[tl].v>=a[i].v) tl--;
q[++tl]=a[i];
}
for(int i=0;i<N-K+1;i++){
while(tl>=hd&&q[tl].v>=a[i+K-1].v) tl--;
q[++tl]=a[i+K-1];
while(q[hd].pos<i) hd++;
printf("%d",q[hd].v);
if(i==N-K) printf("\n");
else printf(" ");
}
hd=tl=0;
q[0]=a[0];
for(int i=1;i<K-1;i++){
while(tl>=hd&&q[tl].v<=a[i].v) tl--;
q[++tl]=a[i];
}
for(int i=0;i<N-K+1;i++){
while(tl>=hd&&q[tl].v<=a[i+K-1].v) tl--;
q[++tl]=a[i+K-1];
while(q[hd].pos<i) hd++;
printf("%d",q[hd].v);
if(i==N-K) printf("\n");
else printf(" ");
}
return 0;
}