> 啥是分塊? 算法
簡單的說, 就是給你一段長度爲 n 序列, 把它分紅 x 叉樹的形式(每層有 x 個塊), 能夠分紅 logx(n) 層; 數組
若是採用 sqrt(n) 叉樹, 就有 2 層; 若是採用 2 叉樹, 就有 logn 層; 優化
第一個是分塊, 第二個是線段樹. spa
> 爲何要分塊? 3d
有 x 次操做, 每次操做複雜度爲 O(a), 有 y 次詢問, 每次詢問複雜度爲 O(b), 咱們就能夠經過提升一邊的複雜度來下降另外一邊的複雜度, 使總體複雜度平衡, 以提高整個算法的效率. blog
舉個例子, 給出一個序列, 進行 m 次操做: 修改某個位置的值, 查詢某段區間的和. 排序
若是直接作, 修改是 O(1) 的複雜度, 查詢是 O(n) 的, 總體複雜度就是 O(m*n), 但咱們利用線段樹平衡兩邊的複雜度, 使修改與查詢都爲 O(logn), 整個算法的複雜度就是 O(mlogn) 了. get
分塊也同理, 平衡兩邊複雜度爲 O(sqrt(n)), 這個過程稱做根號平衡, 因此根號算法的複雜度就是 O(m*sqrt(n)). io
> 分塊解決什麼問題? 效率
例1: 維護一個長度爲 n 的序列, 進行 m 次操做: 區間加, 查詢某段區間內小於 x 的數的個數.
若是是單點修改, 咱們能夠用樹套樹來實現, 但區間修改樹套樹沒法快速合併信息.
若是信息能夠快速合併, 則優先選擇線段樹結構, 畢竟 logn 是優於 sqrtn 的; 而對於沒法快速合併信息的狀況, 就能夠考慮用分塊實現.
這道題, 分塊並將每塊排序. 修改時, 整塊打標記, 零散塊枚舉 + 重構(重構用歸併); 查詢時, 整塊查詢小於 x 的數, 這個整塊的標記爲 y(也就是說這一塊全部數都加了y)則等價於查整塊的排序後的數組裏面小於 x - y 的數的個數, 查找能夠二分, 零散塊就直接暴力查詢塊內在查詢區間內的數是否知足條件.
例2: 維護一個長度爲 n 的序列, 進行 m 次操做: 區間加, 查詢某段區間內第 k 小的數.
解法與例1相似. 查詢第 k 小的數時, 須要二分答案, 而後在每一個整塊上用 lower_bound()求有多少個數比二分出的這個答案小, 在零散塊上枚舉, 累加起來, 看是否是等於 k-1. So easy?
誒, 等等, 每二分一個答案, 就枚舉一遍零散塊, 這樣不是很慢麼? 這樣複雜度 O(logn * sqrtn)的呀.
想辦法優化這個複雜度. 咱們能夠把零散塊歸併成一個假的整塊, 在這個整塊上 lower_bound(), 這樣複雜度就是 O(sqrtn + logn * log(sqrtn))了.
例3: 維護一個序列, 支持 O(1)單點修改, O(sqrt(n))區間和.
分塊維護序列. 直接修改原數組和所在的塊, 查詢整塊 O(1), 零散塊 O(sqrt(n))枚舉.
例4: 維護一個序列, 支持 O(sqrt(n))單點修改, O(1)區間和.
分塊維護前綴和. 維護塊內序列的前綴和, 維護塊的前綴和. 修改當前塊內前綴和與塊的前綴和, 查詢整塊的塊前綴和與零散塊的塊內前綴和.
例5: 維護一個序列, 支持 O(sqrt(n))區間加, O(1)查單點.
分塊維護序列. O(1)加整塊, O(sqrt(n))枚舉加零散塊, O(1)查詢.
例6: 維護一個序列, 支持 O(1)區間加, O(sqrt(n))查單點.
差分. 把 a[i]變爲 a[i] - a[i-1], 區間加至關於修改單點, 查詢單點至關於求前綴和, 轉變成例5.
例7: 維護一個集合, 支持 O(1)插入一個數, O(sqrt(n))查詢第 k 小.
離散化後分塊維護值域. a[i]表示數字 i 出現的次數, 維護每一個塊內有多少個數. 查詢的時候從第一個塊開始往右跑, 最多走過 sqrt(n)個整塊和 sqrt(n)個零散的數.
例8: 維護一個集合, 支持 O(sqrt(n))插入一個數, O(1)查詢第 k 小.
這個題比較有意思, 能夠先想想再看.
考慮到要 O(1)查詢, 可見確定要能直接定位到第 k 小的數的位置.
先離線讀入全部的操做, 把插入的數放在一個序列裏面, 將序列排序後分塊.
用 pos[i]記錄元素 i 在哪一個塊中, 若是 i 有多個則記錄第一個.
用 L[i]和 R[i]分別記錄第 i 個塊的左右端點, 即第 i 個塊裏面是排名 L[i] ~ R[i]的數.
用 belong[i]表示排名第 i 的數在哪一個塊裏.
每插入一個數 i, 就把它放到 pos[i]這個塊的尾部, O(sqrt(n))維護一下塊內的大小順序, O(sqrt(n))維護一下後面塊的左右端點(由於日後移了一位).
詢問時, 輸出 Block[belong[k]][k - L[belong[k]]].
更多題目參見標籤 "分塊", "題解", 不斷更新.