Evanyou Blog 彩带

洛谷P1886 滑动窗口

传送门啦

以最大值为例,既然我们想要保证队列开头为答案,那么我们就要保证每次更新使最大值一直放在队列。那么如果存储的最大值该弹出了怎么办呢?我们只需要记录下每个元素的位置,判断是否在区间内即可。

队头弹出后,第二位就变成了队头,我们就要保证这个数现在是区间内最大。那么是不是说,我们需要将这个长度为 $ k $ 的区间中的每一个数都存起来呢?不是的,并不是每个数被记录都有意义的。举个例子, $ a_i $ 和 $ a_j $ 两个数, $ i<j $ 。如果 $a_i >a_j $,那么两个数都应该记录;但是如果 $ a_i≤a_j $ ,那么当 $ a_j $ 进入区间后, $ a_i $ 的记录就没有意义了。很好理解,我们假设每个数作为区间最大值的次数为它的贡献,当 $ a_j $ 进入区间以后,在区间中存在的时间一定比 $ a_i $ 长,也就说明 $ a_i $ 一定不会再做贡献了,我们确定没有贡献的点,便可以直接删去,以维护单调队列的单调性。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <cmath>
#define re register
using namespace std ;
const int maxn = 1e6 + 5 ;

inline int read () {
	int f = 1 , x = 0 ;
	char ch = getchar () ;
	while ( ch > '9' || ch < '0' ) {if(ch == '-') f = -1 ; ch = getchar () ; }
	while ( ch >= '0' && ch <= '9' ) {x = (x << 1) + (x << 3) + ch - '0' ; ch = getchar () ; }
	return x * f ;
}

int n , k , a[maxn] ;
int head = 1 , tail ;

struct Node {
	int val , pos ;
}q[maxn] ;

int main () {
	n = read () ; k = read () ;
	for(re int i = 1 ; i <= n ; ++ i) {a[i] = read () ;}
	for(re int i = 1 ; i <= n ; ++ i) {
		while(head <= tail && q[head].pos + k <= i)  ++head ;
		while(head <= tail && q[tail].val >= a[i]) --tail ;
		q[++tail].val = a[i] ;
		q[tail].pos = i ;
		if(i >= k)  printf("%d " , q[head].val) ;
	}
	head = 1 , tail = 0 ;
	printf("\n") ;
	for(re int i = 1 ; i <= n ; ++ i) {
		while(head <= tail && q[head].pos + k <= i) ++head ;
		while(head <= tail && q[tail].val <= a[i]) --tail ;
		q[++tail].val = a[i] ;
		q[tail].pos = i ;
		if(i >= k) printf("%d " , q[head].val) ;
	}
	return 0 ;
}
posted @ 2019-02-21 19:45  Stephen_F  阅读(128)  评论(0编辑  收藏  举报