分塊演算法 優雅的暴力
參考:http://blog.csdn.net/XianHaoMing/article/details/52201698
分塊
先簡單介紹一下分塊演算法。
分塊演算法是一種很常見的根號演算法,一般它的時間複雜度會帶根號。
分塊和線段樹的區別在於,分塊演算法可以維護一些線段樹維護不了的東西,例如單調佇列等,線段樹能維護的東西必須能夠進行資訊合併,而分塊則不需要。不過,它們也有共同點,分塊和線段樹一樣,分塊需要支援類似標記合併的東西。
簡單來說,分塊演算法就是優化過後的暴力。
現在講一下這種演算法的實現。
這種演算法會將序列(序列長度為N)進行分塊,通常設定一個上限K,每一塊有至多K個元素。在序列分塊問題上,一般會嚴格要求每個塊都要有K
我們一般都會設K=N−−√,這樣塊數也就只有NK=N−−√塊數可能多一。
通常實現時,我們用bei表示第i個位置所屬的塊。對於每個塊都進行資訊維護。
單點修改時,我們一般先將對應塊的標記下傳,再暴力更新被修改塊的狀態。
時間複雜度O(n−−√)。
如果是區間[L,R]修改的話,對於被[L,R]整塊跨過的塊直接打標記,兩端剩餘的部分暴力重構塊的狀態即可。
中間最多經過n−−√塊,兩邊暴力修改也是n−−√次的,所以時間複雜度為O(n−−√)。
至於詢問操作,和區間修改類似,對於中間跨過的整塊,直接利用塊儲存的資訊統計答案,兩端剩餘部分任然可以暴力掃描統計。
時間複雜度和區間修改一樣,也是O
如果詢問次數為m,那總的時間複雜度即為O(mn−−√)。
1.思想
如果我們需要對一個特定的序列進行操作,那麼非常直觀、簡單的方法就是純暴力(不,那叫模擬)。
不過如果暴力能過的話,那就呵呵了。
所以我們要想一些比較高能的資料結構——分塊。
相比線段樹來說,分塊演算法比較難實現,但是隻要深入理解,就可以實現了,只不過需要一些資料結構的輔助。
分塊實質來說就是把一個序列切分,從而實現對查詢、查詢、替換等等操作的高效處理。
模板
int n; scanf("%d",&n); int block=sqrt(n); int cnt=0; if(n%block) cnt=n/block+1; else cnt=n/block; for(int i=1;i<=n;i++) scanf("%d",&k[i]); for(int i=1;i<=cnt;i++) { l[i]=(i-1)*block+1,r[i]=i*block; } r[cnt]=n; for(int i=1;i<=n;i++) belong[i]=(i-1)/block+1;