1. 程式人生 > >分塊演算法 優雅的暴力

分塊演算法 優雅的暴力

參考:http://blog.csdn.net/XianHaoMing/article/details/52201698

分塊

先簡單介紹一下分塊演算法。 
分塊演算法是一種很常見的根號演算法,一般它的時間複雜度會帶根號。 
分塊和線段樹的區別在於,分塊演算法可以維護一些線段樹維護不了的東西,例如單調佇列等,線段樹能維護的東西必須能夠進行資訊合併,而分塊則不需要。不過,它們也有共同點,分塊和線段樹一樣,分塊需要支援類似標記合併的東西。 
簡單來說,分塊演算法就是優化過後的暴力。

現在講一下這種演算法的實現。 
這種演算法會將序列(序列長度為N)進行分塊,通常設定一個上限K,每一塊有至多K個元素。在序列分塊問題上,一般會嚴格要求每個塊都要有K

個元素,這樣就會分成約NK塊。(最後一個塊除外)

我們一般都會設K=N,這樣塊數也就只有NK=N塊數可能多一。 
通常實現時,我們用bei表示第i個位置所屬的塊。對於每個塊都進行資訊維護。

單點修改時,我們一般先將對應塊的標記下傳,再暴力更新被修改塊的狀態。 
時間複雜度O(n)。

如果是區間[L,R]修改的話,對於被[L,R]整塊跨過的塊直接打標記,兩端剩餘的部分暴力重構塊的狀態即可。 
中間最多經過n塊,兩邊暴力修改也是n次的,所以時間複雜度為O(n)。

至於詢問操作,和區間修改類似,對於中間跨過的整塊,直接利用塊儲存的資訊統計答案,兩端剩餘部分任然可以暴力掃描統計。 
時間複雜度和區間修改一樣,也是O

(n)。

如果詢問次數為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;