CF1106E Lunar New Year and Red Envelopes
阿新 • • 發佈:2020-09-02
咕咕咕咕的鴿子 MK 來填坑了……
先看例題:P1886 滑動視窗 /【模板】單調佇列
注:摘要圖片為例題中的圖片 QwQ。
emm,建議先看看關於單調棧的部落格,便於理解單調佇列。
所以單調佇列就是佇列內元素具有單調性的佇列。而單調佇列的用處就是求區間內的最大/最小值。
那麼怎麼求呢?以求區間最大值為例,我們不妨讓隊首為我們要的答案,那麼這就是一個單調遞減的佇列。
有一句話叫:“如果一個人比你小還比你強,那麼你就永遠無法超越他了。”什麼意思呢?就是說,如果一個元素比你後入隊還比你大,你就永遠無法成為最大了,那麼你就沒用了,就可以 退役 出隊了。而每次佇列新加入的元素肯定是後入隊的,所以在入隊之前把隊尾和這個元素做比較:若比這個元素小(在求最大值的情況下是小),那麼就把隊尾出隊,直到隊尾比這個元素大(如果比他大就可能成為最大值)或佇列為空,然後把這個元素入隊(從隊尾)(一定要入隊,因為在前面的 退役
你以為這樣就結束了?NoNoNo!我們還要在每次入隊的時候判斷隊首是否不在這個區間裡。這裡只要一個if
而不是迴圈,因為每次都這樣判斷可以保證最多隻有一個(也就是隊首)不在區間內。
上程式碼吧:
#include<bits/stdc++.h> using namespace std; int n,k,a[1000005];//原陣列 deque<pair<int,int> >d1; deque<pair<int,int> >d2; //d1,d2分別為求最大值,最小值的單調佇列,佇列裡的第一個關鍵字儲存值,第二個關鍵字儲存這個元素在a[]陣列中的位置(下標) queue<int>qmin; queue<int>qmax; //由於後面是一起做的,所以用佇列儲存答案(當然可以用陣列) int main() { cin>>n>>k; for(register int i=1;i<=n;i++) cin>>a[i];//讀入元素 for(register int i=1;i<k;i++) { while(!d1.empty()&&d1.back().first<a[i]) d1.pop_back(); while(!d2.empty()&&d2.back().first>a[i]) d2.pop_back(); d1.push_back(make_pair(a[i],i)); d2.push_back(make_pair(a[i],i)); }//先把前k-1個入隊 if(k==1)//見後面的while,發現需要特判k=1的情況 { d1.push_back(make_pair(a[1],1)); d2.push_back(make_pair(a[1],1)); qmin.push(a[1]); qmax.push(a[1]); } while(d1.back().second<n)//表示後面還有元素 { int xx1=a[d1.back().second+1],yy1=d1.back().second+1; int xx2=a[d2.back().second+1],yy2=d2.back().second+1; //分別記錄兩個單調佇列的隊尾的元素大小和在a[]裡的序號 while(!d1.empty()&&d1.back().first<xx1) d1.pop_back(); while(!d2.empty()&&d2.back().first>xx2) d2.pop_back(); //讓那些又老又弱的元素出隊 if(!d1.empty()&&d1.front().second<yy1-k+1) d1.pop_front(); if(!d2.empty()&&d2.front().second<yy2-k+1) d2.pop_front(); //讓不在區間內的元素出隊 d1.push_back(make_pair(xx1,yy1)); d2.push_back(make_pair(xx2,yy2)); //將新元素入隊 qmin.push(d1.front().first); qmax.push(d2.front().first); //儲存答案 } while(!qmax.empty()) { cout<<qmax.front()<<' '; qmax.pop(); } cout<<'\n'; while(!qmin.empty()) { cout<<qmin.front()<<' '; qmin.pop(); } //輸出答案 return 0; }