【題解】洛谷P1886 滑動視窗(單調佇列)
阿新 • • 發佈:2018-12-13
(之前從未聽說過這道題目 來到qbxt後大佬們都早就切掉此題了 倍感慚愧qwq)
題目大意就是給定一個序列A與要求的長度k,讓我們輸出A中所有長度為k的區間的最大值和最小值。
看到資料範圍後我們發現暴力會炸掉,所以要考慮比較簡潔的方法。這裡我們維護一個元素單調遞減的佇列求區間內的最大值,最後單調佇列隊首的元素一定是最大值。進一步地,插入了第i個元素後的單調佇列隊首元素一定是前i個元素的最大值,這就意味著單調佇列可以求出區間右端點向右移動的區間最值,我們再考慮如何讓區間左端點右移。
•有兩個比較顯而易見但是很有用的結論:
•1、單調佇列中的元素不僅值單調,在原序列中的位置也單調。
•2
•那麼方法就有了:當隊首的元素不在當前處理的區間內時,刪去隊首的元素,重複判斷,直到隊首的元素在當前的區間內為止。
•於是我們得到了一個線性的演算法。
PS:注意第一個點是可以直接放進去的,所以我們讓head=1,tail=0,初始化單調佇列。
#include<cstdio> #include<iostream> #include<algorithm> #include<cstring> #include<cstdlib> using namespace std; const int maxn=1000010; int a[maxn]; int p[maxn],num[maxn]; //priority_queue & number int head=1,tail=0; int n,k; void found_minn() { head=1; tail=0; memset(p,0,sizeof(p)); memset(num,0,sizeof(num)); for(int i=1;i<=n;i++) { while(head<=tail&&p[tail]>=a[i]) { tail--; } p[++tail]=a[i]; num[tail]=i; while(num[head]<=i-k) { head++; } if(i>=k) printf("%d ",p[head]); } } void found_maxx() { head=1; tail=0; memset(p,0,sizeof(p)); memset(num,0,sizeof(num)); for(int i=1;i<=n;i++) { while(head<=tail&&p[tail]<=a[i]) { tail--; } p[++tail]=a[i]; num[tail]=i; while(num[head]<=i-k) { head++; } if(i>=k) printf("%d ",p[head]); } } int main() { scanf("%d%d",&n,&k); for(int i=1;i<=n;i++) { scanf("%d",&a[i]); } found_minn(); printf("\n"); found_maxx(); return 0; }