题解 P1886 【滑动窗口】

Xiefan_Guo

2019-08-10 21:15:47

Solution

**经典的单调队列问题** 单调队列需要满足的条件: * 队列中的元素其对应在原来的列表中的顺序必须是单调递增的。 * 队列中元素的大小必须是单调递(增/减/自定义)。 --- $que[]$ 表示单调队列,$id[]$ 表示所对应在原列表里的序号。 > 当然,实际上只保留 $id[]$ 就足够了。一般的我们也时这么做的。 对于样例我们有: ``` 8 3 1 3 -1 -3 5 3 6 7 ``` 1. 此时队中没有一个元素,我们令 $1$ 进队。 $que=\{1\},id=\{1\}$ 。 2. 假如把 $3$ 放进去,如果后面 $2$ 个数都比它大,那么 $3$ 就有可能成为最小的。此时,$que=\{1,3\},id=\{1,2\}$。 3. $que=\{-1\},id=\{3\}$。 4. $que=\{-3\},id=\{4\}$。 5. $que=\{-3,5\},id=\{4,5\}$。 6. $que=\{-3,3\},id=\{4,6\}$。 7. $que=\{3,6\},id=\{6,7\}$。 8. $que=\{3,6,7\},id=\{6,7,8\}$。 ``` #include<bits/stdc++.h> using namespace std; const int maxn = 1000005; int que[maxn], id[maxn], a[maxn]; int n, k; void solve_min() { int l = 1, r = 1; for(int i = 1 ; i <= n; i++){ while(l < r && a[i] < que[r - 1]) r--; que[r] = a[i]; id[r++] = i; while(id[l] <= i - k) l++; if(i >= k) printf("%d%c", que[l], i == n ? '\n' : ' '); } } void solve_max() { int l = 1, r = 1; for(int i = 1 ; i <= n; i++){ while(l < r && a[i] > que[r - 1]) r--; que[r] = a[i]; id[r++] = i; while(id[l] <= i - k) l++; if(i >= k) printf("%d%c", que[l], i == n ? '\n' : ' '); } } int main() { scanf("%d%d", &n, &k); for(int i = 1; i <= n; i++){ scanf("%d", &a[i]); } solve_min(); solve_max(); return 0; } ```