P1886 滑動窗口 /【模板】單調隊列 題解

博客園同步

原題鏈接

簡要題意:

給定一個數組,求所有連續 mm 個數的最大值和最小值。

首先,對於這種題目,用 5+5+ 種方法(至少),這裏介紹幾種吧。

算法一

根據 RMQ\texttt{RMQ} 算法解決問題。

fi,jf_{i,j} 表示從 ii 開始往後 2j2^j 個的最大值。(2j>n2^j > n 則視爲 nn

那麼,顯然有:

fi,j={ai,j=0ffi,j1,j1,j0 f_{i,j} = \begin{cases} a_i , j=0 \\ f_{f_{i,j-1},j-1} , j \not = 0 \\ \end{cases}

其中,fi,j1f_{i,j-1} 即往後先走 2j12^{j-1} 個, 再走 2j12^{j-1} 個,類似於 倍增 算法。

然後,預處理 log\log,即可 O(nlogn)O(n \log n) 初始化,O(1)O(1) 回答。

時間複雜度:O(nlogn+k)O(n \log n + k).

期望得分:50pts50pts ~ 100pts100pts.(取決於程序常數,聽說這道題目 卡任何帶 log\log 的算法,不知道能不能卡過去)

算法二

分塊,本題效率最慢的算法。

請不要輕看它,不信? 還有這個貼

顯然,分成 n\sqrt{n} 塊,對每塊分別計算,對於 nk+1n-k+1 個塊分別詢問。

時間複雜度:O((nk+1)n)O((n-k+1) \sqrt{n}), 如果是極限數據 n=106n=10^6k=1k=1 就直接卡掉了。(kk 很小就可以)

期望得分:50pts50pts ~ 100pts100pts.

實際得分:92pts92pts.(有人實測過,卡常得高分)

算法三

線段樹,在分塊與 RMQ\text{RMQ} 之間。

這題的算法上有線段樹但是過不了。。。

不用說了吧,模板在此.

時間複雜度:O(nlogn+klogn)O(n \log n + k \log n). (顯然被卡超時,不過:)

期望得分:50pts50pts ~ 100pts100pts.

實際得分:92pts92pts.(真·卡常大法好)

算法四

指令集。???

洛穀日報——指令集

學習完之後,相信你能隨便用一個數據結構(甚至是暴力???)切掉本題!

時間複雜度:O(n2)O(n^2) ~ O(nlogn+klogn)O(n \log n + k \log n) ~ O(nlogn+k)O(n \log n + k).

期望得分:100pts100pts.

實際得分:92pts92pts ~ 100pts100pts.(衆所周知數據加強過一次,要是加強之前交沒準就過了,不然沒準被卡)

算法五

扯了這麼多。。終於到正題了!

單調隊列 爲什麼叫這麼名字,它不是白叫的好吧 像平衡樹要是不平衡那要它幹麼呢

假設,現在一個君王他叫做 CCFCCFCCF=Centre Code ForcesCCF = \text{Centre Code Forces}),有 88 名選手,她們(對,就是她)的才華值分別爲:

1 3 -1 -3 5 3 6 7

她們從左往右依次是:zhk,yy,kkk,bfw,gyx,cz,wxq,rxz.

然後,他們參加 CCFCCF 比賽的年齡分別爲 88 ~ 11 歲。(嗯?沒錯)

CCFCCF 有一個 怪癖:只要一個選手參加 CCFCCF 比賽超過 33 年,她(?)就覺得該選手沒有潛力了。

  • zhk88 歲的時候進入了,他發現沒有一個人比他更厲害,所以他留下來了。

  • yy77 歲的時候也進入了 CCFCCF 比賽,並把 zhk 踢下去,因爲 3>13>1. zhk 想:他比我小還比我強,我退役吧,然後就退役了。

  • kkk66 歲時一來,發現被 yy 吊打了(3>13 > -1),但是他想:你比我強,但是我比你小,我更有潛力,我活的比你久,沒準等你退役我就是老大! 於是留了下來。然後,CCF Round 1\text{CCF Round 1} 冠軍產生:yy.

  • bfw 一看:嗯?怎麼前面的人都比我強啊?事實 然後他覺得,自己留下來的時間更久,等她們都退役再說。 於是留了下來。CCF Round 2\text{CCF Round 2} 冠軍產生:yy.

  • gyx 來了之後,他發現自己是最強的 5>35>3,然後所有的人(除了她)都覺得 有人比我小還比我強,集體退役。yy 又遭到 CCFCCF 的潛力質疑,精神病發作了。CCF Round 3\text{CCF Round 3} 冠軍產生:gyx.

  • cz 來了之後,嗯發現自己是最弱的,但是她想: CCF質疑 gyx 比我前,所以我比她活得長!活得長就是報仇,所以要留下來!CCF Round 4\text{CCF Round 4} 冠軍產生:gyx.

  • wxq 一到,瞬間 吊打集訓隊吊打全場然後 gyxcz 一起退役了。CCF Round 5\text{CCF Round 5} 冠軍產生:wxq.

  • 最後 rxz 到了,結束了 wxqCCFCCF 生涯,把她弄自閉然後滾出集訓隊了。(我纔不會告訴你 rxz 就是 CCFCCF 出題人CCF Round 6\text{CCF Round 6} 冠軍產生:rxz.

若干年過去了,CCF\text{CCF} 決定:分數越低的人得獎越高,那些強者實在弱不下來,於是又開始了第二場 互逼退役 的旅程。。

綜上可見,單調隊列的過程本質就是一個維護隊列的過程,參加 CCFCCF 則入隊,退役則出隊。

顯然,如果用系統的 queue\text{queue} 寫會多出一個 log\log,然後變成 O(nlogn)O(n \log n) 級別,淪爲和上述數據結構一樣的。(智商不夠,數據結構來湊

所以今天我們用 手寫隊列,用 l,rl,r 指針維護當前在隊列中的元素。

時間複雜度:O(n+k)O(n + k ).

實際得分:100pts100pts.

#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;

const int N=1e6+1;

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

int n,m,a[N];
int q[N],p[N]; //q[i] 記錄數值 , p[i] 記錄編號

int main(){
	n=read(),m=read();
	for(int i=1;i<=n;i++) a[i]=read();
//	for(int i=1;i<=n;i++) printf("%d ",a[i]); puts("");
	int l=1,r=0;
	for(int i=1;i<=n;i++) {
		while(l<=r && q[r]>=a[i]) r--; //新來的人先吊打掉一波
		q[++r]=a[i]; p[r]=i;
		while(p[l]<=i-m) l++; //被質疑的可以直接退役
//		printf("%d %d\n",l,r);
		if(i>=m) printf("%d ",q[l]);
	} puts(""); l=1;r=0;
	for(int i=1;i<=n;i++) {
		while(l<=r && q[r]<=a[i]) r--;
		q[++r]=a[i]; p[r]=i;
		while(p[l]<=i-m) l++;
		if(i>=m) printf("%d ",q[l]);
	}  //這邊同理
	return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章