學習筆記——莫隊
前言
莫隊演算法是一種優雅的暴力演算法,因其發明人是國家隊隊長莫濤,所以叫莫隊演算法。他是通過離線詢問的方式將詢問分塊,再暴力維護的一種資料結構。
演算法
思想:先離線將每一個詢問儲存下來,將其按區間左端點從小到大(左端點相同則比較右端點)的順序排序,之後維護兩個指標$l,r$暴力維護每一個區間即可。
時間複雜度:因為一共分成了$\sqrt{n}$個塊,最壞情況下從每一個塊的起點遍歷到序列的右端點,則複雜度為$O(n\sqrt{n})$。
例題:
本題為莫隊板子,照著題面直接模擬即可。
P5268 [SNOI2017]一個簡單的詢問(題解)(我的程式碼)
本題運用差分,可以將$get(l_1,r_1,x)$轉化為$get(1,r_1,x)-get(1,l_1-1,x)$,令$g(i,x)=get(1,i,x)$,則原式可以轉化為
$$\sum\limits_{x=1}^{\infty}g(r_1,x)g(r_2,x)-g(r_1,x)g(l_2-1,x)-g(r_2,x)g(l_1-1,x)+g(l_1-1,x)g(l_2-1,x)$$
然後拆成四份分別用莫隊進行求解。
帶修莫隊
思想:對於每一次修改操作,我們可以引入一個時間戳代表是第幾次修改,離線後我們就可以維護三個指標$l,r,time$,當三個指標都移動到正確位置後再統計答案。
注意:此時塊長設為$\sqrt[3]{n^4t}$
例題
P1903 [國家集訓隊]數顏色 / 維護佇列(題解)(我的程式碼)
樹上莫隊
思想:莫隊可以處理一維的序列問題,對於樹上的問題,我們也可以運用$dfs$序將其轉化為一位的序列再進行求解。
例題
SP10707 COT2 - Count on a tree II(題解)(我的程式碼)
本題要對樹上的兩點路徑進行操作,所以我們引入尤拉序:在遍歷整棵樹時該節點第一次被遍歷則加入序列,遍歷完其子樹後再將其加入序列。
通過尤拉序,我們就可以統計$u,v$在尤拉序中第一次出現的位置的區間內只出現過一次的點,即為$u,v$兩點的路徑。
注意:若$u,v$兩點的$LCA$不是$u$或$v$,則需要統計$u$最後一次出現到$v$第一次出現的區間內只出現一次的點,這些點才是$u,v$兩點的路徑。
P4689 [Ynoi2016] 這是我自己的發明(題解)(我的程式碼)
本題是P5268 [SNOI2017]一個簡單的詢問的樹上加強版,我們同樣可以運用$dfs$序將樹上的問題轉化到一個序列中進行求解。
對於換根操作,我們可以分類討論:
-
$root$不在$u$的子樹內,則該區間就是以1為根的樹$u$所對應的子樹的區間。
-
$root$在$u$的子樹內,則需要找到$u$的一個兒子節點$v$,使$root$在$v$的子樹中,則區間為[1,n]減去$v$所在子樹所包含的區間。
設$f(a,b,c,d)$為$[a,b]$和$[c,d]$兩區間內點權相同的個數和,$g(x,y)=f(1,x,1,y)$,$h(i)=g(i,n)$
- $u,v$都是一段區間:
$$f(l_1,r_1,l_2,r_2) = g(r_1,r_2) - g(l_1-1,r_2)-g(l_2-1,r1)+g(l_1-1,l_2-1)$$
- $u,v$一個為一段區間,另一個為兩段區間
$$f(l_1,r_1,1,l_2)+f(l_1,r_1,r_2,n) = g(r_1,l_2) - g(l_1-1,l_2)-g(r_1,r_2-1)+g(l_1-1,r_2-1)+h(r_1)-h(l_1-1)$$
- $u,v$都是兩段區間:
$$f(1,l_1,1,l_2)+f(1,l_1,r_2,n)+f(r_1,n,1,l_2)+f(r_1,n,r_2,n) = g(l_1,l_2) - g(l_1,r_2-1)-g(r_1-1,l_2)+g(r_1-1,r_2-1)+h(l_1)+h(l_2)+h(n)-h(r_1-1)-h(r_2-1)$$
P4074 [WC2013] 糖果公園(題解)(我的程式碼)
本題是樹上帶修莫隊,同樣使用尤拉序將樹上兩點間路徑轉化為區間內出現奇數次的節點,套帶修莫隊板子進行維護即可。
回滾莫隊
思想:對於一些新增至容易但刪除困難的操作,我們可以每一次將$l$移動到區間的右端點$+1$的位置,將$r$移動到右端點,因為莫隊的排序中將左端點在同一塊中的所有查詢區間右端點單調遞增排序,所以我們可以用正常的莫隊方法移動右端點,再記錄當前的$res$(設為$tmp$),暴力將左端點移動至正確位置,統計答案後將左端點移回區間的右端點$+1$的位置,將$res$改為$tmp$即可。
例題
P5906 【模板】回滾莫隊&不刪除莫隊(題解)(我的程式碼)
學習資料
部落格:莫隊演算法——從入門到黑題
題單:【演算法】莫隊