莫隊演算法講解 (詳盡版)
莫隊演算法我早有耳聞。。可惜前不久才去學習。
但是自己看了看論文,也就1h左右,就能夠全部理解了。
也就是說其實這個演算法不難。。
好了,讓我們進入正題。
我們首先來看一道例題:
Description
有n個數字,給出k,以及m個查詢。
每次查詢的格式是L,r,求L~r(左右包含)這個區間內數字的出現次數剛好是k的數字種數。
範圍:n<=30000,k<=n,m<=30000,1<=L<r<=n,數列中元素大小<=n。
輸入n,k,m,然後n個元素,然後m行查詢,對於每一個詢問,輸出正確的答案。
Example input:
5 2 3
1 2 3 2 2
1 2
2 4
1 5
Example output:
0 //沒有
1 //2
0 //沒有(2太多了,也不算)
我們來考慮一下這題的解法。
首先肯定可以萬能暴力,每次L~r列舉。時間複雜度O(N*M)。
但是這樣的暴力是沒有前途的!我們來考慮一下新的暴力:
一開始指標區間0->0,然後對於一個查詢,我們將Left指標逐步更新成新的L,Right同理。
比如一開始Left=2,Right=3,而L=1,r=5。
那麼我們Left-1,並且把Left位置上的數字出現次數+1.
Right+1,把Right位置上的數字出現次數+1,直到Right=5為止。
框架:
add(x){ //把x位置的數字加入進來
cnt[x]++;
if (cnt[x]==k) ans++;
}
remove(x){ //把x位置的數字移出去
cnt[x]--;
if (cnt[x]==k-1) ans--;
}
然後以上面題目為例;這種方法需要離線處理,我們同理來看一下解法:
Left=Right=1; add(1); ans=0; for u=1 to m{ while (Left<L[u]){ remove(Left); Left++;} while (Left>L[u]){ Left--; add(Left);} while (Right<r[u]){ Right++; add(Right};} while (Right>r[u]){ remove(Right); Right--;} output ans; }
這裡說明一下,其實remove(Left);Left--;等等可以直接寫成remove(Left--)等等,我這麼寫是為了理解。
分析一下時間複雜度,我們可以從Left和Right的移動量來分析:
每一個新的詢問,Left和Right的移動量最大都會是O(N)
所以這樣子的方法時間複雜度仍然是O(N*M),而且可能比上面的暴力更慢。
但是莫隊演算法的核心,就是從這麼一個演算法轉變過來的。
現在來介紹一下莫隊演算法解決這道題:
對詢問進行分塊,我們知道m個詢問,L和r的範圍都在n以內,我們根據L和r的大小來對詢問分塊。
比如n=9,有以下的詢問:
2 3
1 4
4 5
1 6
7 9
8 9
5 8
6 8
對於n=9,我們以根號n為每個塊block的大小,這裡block=3.
那麼我們把1~3分成一組,4~6,7~9.
對於每一個詢問(L,r),我們以L的範圍來決定這個詢問在哪一個塊。
然後每一個獨自的塊內,我們讓詢問r更小的排在更前面。
那麼上面的詢問就可以分組成:
(2,3)/(1,4)/(1,6)和
(4,5)/(5,8)/(6,8)和
(7,9)/(8,9)
這一步的排序操作,我們可以在排序的時候加入判斷條件cmp:
bool cmp(Query x,Query y){
if ((x/block)!=(y/block))
return x.L<y.L; //不同塊的時候
return x.r<y.r; //同一塊的時候
}
排序之後,我們再來分析一下時間複雜度;接下來我們會看到神奇的事情!!
剛才分析此方法的時候,我們是從L和R的偏移量分析的;我們仍然用這種方法來分析。
考慮一下在同一個塊的時候。由於L的範圍是確定的,所以每次L的偏移量是O(√N)
但是r的範圍沒有確定;r的偏移量是O(N)。
那麼從一個塊到另一個塊呢?
明顯地,r我們不需要作考慮,仍然是O(N)。
而L明顯最多也是2*√N,而且這種情況下,很快就會到下下一塊。所以也是O(√N)
由於有√N(根號N)個塊,所以r的總偏移量是O(N*√N)
而M個詢問,每個詢問都可以讓L偏移O(√N),所以L的總偏移量O(M*√N)
注意了,時間複雜度分析的時候一定要注意,r的偏移量和詢問數目是沒有直接關係的。
而L則恰恰相反;L的偏移量我們剛才也說明了,它和塊的個數沒有直接關係。
所以總的時間複雜度是:
O((N+M)*√N)
很神奇地看到了,我們僅僅改變了一下問題求解的次序,就讓時間複雜度大幅度下降!
當然在這個說明過程中我們也看到了,事實上,莫隊是一個必須離線的演算法。
意味著一些題目如果強制線上,那麼莫隊就無能為力了。
程式碼就不總結了,可以看上面的分塊的地方,也可以看一些莫隊的題目的題解。