分塊學習筆記
\[\Large 演算法分析 \]
\(\quad\)考慮一些在序列上的區間操作,如區間加和區間求和。
\(\quad\)直接暴力做單次操作複雜度是 \(\Theta(n)\),總時間複雜度 \(\Theta(qn)\).
\(\quad\)如果將序列分成 \(\sqrt{n}\) 塊,每塊長度為 \(\sqrt{n}\),並且對每個塊維護裡面所有元素的和,即可在 \(\Theta(\sqrt{n})\) 的複雜度內進行單次操作。
\(\quad\)具體實現如下:
-
令塊的數量為 \(num=\sqrt{n}\),單個塊的長度為 \(siz=\lceil\frac{n}{num}\rceil\)
-
記 \(bel_i\) 表示 \(i\) 號節點屬於第幾個塊。
-
可以記錄一些題目需要用到的資訊,如 \(sum_i\) 表示第 \(i\) 個塊內所有元素的和。
\[\Large \rm [TJOI2009]開關 \]
\(\quad\)記 \(c_i\) 表示第 \(i\) 盞燈是否開著,\(sum_i\) 表示第 \(i\) 個塊中有多少盞燈開著,\(d_i\) 表示第 \(i\) 個塊的狀態是否整體改變。
\(\quad\)對於區間取反操作,零散操作直接修改 \(c_i\),並根據 \(c_i\) 和 \(d_i\) 決定 \(sum_i+1\) 或 \(-1\). 整塊操作不用改變 \(c\)
\(\quad\)對於區間求和操作,零散點求和直接加上 \(c_i\oplus d_i\),整塊求和則加上 \(sum_i\).
\(\quad\)單次操作複雜度 \(\Theta(\sqrt{n})\),總時間複雜度 \(\Theta(q\sqrt{n})\).
\[\Large \rm [LG3396]雜湊衝突 \]
\(\quad\)轉化題意,設計一種資料結構,支援:
-
單點修改
-
求從某一點 \(pos(pos\leqslant d)\) 開始,以步長 \(d\) 遍歷數列,求所經過的所有數之和。
\(\quad\)考慮根號分治。
\(\quad\)對於 \(d\geqslant \sqrt{n}\) 的詢問,直接暴力查詢。
\(\quad\)對於 \(d \leqslant \sqrt{n}\) 的詢問,記 \(sum_{i,j}\) 表示從 \(i(i\leqslant \sqrt{n})\) 開始,步長為 \(j(j\leqslant \sqrt{n})\) 的答案。預處理的時候直接列舉計算即可,時間複雜度 \(\Theta(n\sqrt{n})\).
\(\quad\)對於修改操作 \(a[pos]\leftarrow x\),先直接在原數列上進行修改,再考慮其對 \(sum\) 陣列造成了什麼影響。易知,對於步長為 \(d\) 的預處理陣列,只會對 \(sum_{pos\%d,d}\) 造成影響,於是對每個 \(d(d\leqslant \sqrt{n})\) 修改一下即可,單次修改時間複雜度 \(\Theta(\sqrt{n})\).
\(\quad\)總時間複雜度 \(\Theta[(n+q)\sqrt{n}]\).
\[\Large \rm [LG2801]教主的魔法 \]
\(\quad\)考慮分塊。
\(\quad\)記原數列為 \(A\),將 \(A\) 複製一份記為 \(B\),將 \(B\) 分塊,塊內排序。
\(\quad\)對於修改操作,整塊直接打標記,散點修改完後再塊內排序,這樣完成操作後仍能保持分塊數列的性質,單次操作複雜度 \(\Theta(\sqrt{n}\log\sqrt{n})\).
\(\quad\)對於查詢操作,二分查詢第 \(i\) 個塊內有多少個元素不小於 \(k-add_i\),單次操作複雜度 \(\Theta(\sqrt{n}\log \sqrt{n})\).
\(\quad\)總時間複雜度 \(\Theta[(n+q\sqrt{n})\log n]\).
\(\quad\)考慮再對複雜度做一些優化,令塊的大小為 \(s\),則修改操作若採用歸併排序,單次操作複雜度為 \(\Theta(s+\frac{n}{s})\),同時單次查詢複雜度為 \(\Theta(s+\frac{n}{s}\log s)\). 當詢問次數為 \(q\) 時,總複雜度為 \(\Theta[q(s+\frac{n}{s}\log n)]\geqslant \Theta(q\sqrt{n\log n})\),取最小值當且僅當 \(s=\sqrt{n\log n}\).
\[\Large \rm [國家集訓隊]排隊 \]
\(\quad\)分塊 \(+\) 樹狀陣列。
\(\quad\)考慮交換兩個數 \(a_u\) 和 \(a_v\) 的貢獻:
-
顯然 \(u\) 左邊的數和 \(v\) 右邊的數沒有貢獻。
-
對於 \(a_u\) 來說,貢獻為 \(u\) 到 \(v\) 之間比它大的數的數量減去比它小的數的數量。
-
對於 \(a_v\) 來說,貢獻為 \(u\) 到 \(v\) 之間比它小的數的數量減去比它大的數的數量。
\(\quad\)至於如何計算 \(u\) 到 \(v\) 之間比某個數大或小的值的數量,分塊後散塊直接暴力,整塊建兩顆樹狀陣列即可。
\(\quad\)令塊大小為 \(siz\),則時間複雜度為 \(\Theta[nlogn+q(siz+\frac{n}{siz}logn)]\geqslant nlogn+q\sqrt{nlogn}\),取最小值當且僅當 \(siz=\sqrt{nlogn}\),此時塊的數量 \(num=\sqrt{\frac{n}{logn}}\).
\[\Large \rm [LG4168]Violet蒲公英 \]
\(\quad\)考慮分塊,記 \(s_{i,j}\) 表示前 \(i\) 個塊中顏色為 \(j\) 的數量,\(p_{i,j}\) 表示第 \(i\) 個塊到第 \(j\) 個塊的眾數。
\(\quad\)對於一次詢問,先求出整塊中的眾數、眾數的出現次數和所有在零散塊中出現了的顏色的數量。顯然,如果零散塊中出現的顏色沒有一個比眾數出現的次數多,那麼要求的眾數就是整塊的眾數,否則是出現次數最多的顏色。
\(\quad\)時間複雜度 \(\Theta(n\sqrt{m})\). 此時塊大小 \(B=\frac{n}{\sqrt{m}}\).