1. 程式人生 > >數列分塊入門1 解題報告

數列分塊入門1 解題報告

寫在前面

數列分塊是個好東西。。。我這裡詳細介紹一下分塊演算法,便於初學者的理解(我這個蒟蒻原來也是看不懂分塊)。

分塊簡要介紹

先把陣列分成幾個塊塊,然後就可以對它們整體操作啦。

也就是說,把一個長度為的陣列,拆分成一個個長度為sqrt(n)小塊(當然,最後一塊可能不完整,但是不用管),記錄每個數所屬的塊;也就是這樣——(方便起見,我們直接再開一個數組來記錄所屬分塊,雖然本題中可以臨時計算,但在有些題目中這一步顯得尤為重要)

scanf( "%d", &n );
m = (int)sqrt(n);
for ( int i = 1; i <= n; ++i ) p[i] = ( i - 1 ) / m + 1;
for ( int i = 1; i <= n; ++i ) scanf( "%d", &a[i] );

然後,就可以瞎暴力辣!

實際上,所有的分塊都是這樣。把一個數列分成幾塊,然後對它們進行批量處理。一般來說,我們直接把塊大小設為sqrt(n),但實際上,有時候我們要根據資料範圍、具體複雜度來確定。

正題

當有修改時,對於完整的塊,直接維護一個數組v記錄整個塊加過的數(每塊共同的加數),不完整的就直接暴力在原陣列a上直接加。詢問時,直接把原陣列的值+所屬塊的共同加數即可。

程式碼

#include<cstdio>
#include<cmath>
using namespace std;
#define MAXN 50005

int n, a[MAXN], p[MAXN], m, v[300];
int opt, l, r, c;

void Add( int l, int r, int c ){
    if ( p[l] == p[r] ){//同屬一分塊時直接暴力即可
        for ( int i = l; i <= r; ++i ) a[i] += c;
        return;
    }
    for ( int i = l; p[i] == p[l]; ++i ) a[i] += c;//對於兩邊不完整(即使完整也不管,看做不完整)的分塊,直接暴力即可
    for ( int i = r; p[i] == p[r]; --i ) a[i] += c;
    for ( int i = p[l] + 1; i <= p[r] - 1; ++i ) v[i] += c;//記錄完整分塊的共同加數
}

int main(){
    scanf( "%d", &n );
    m = (int)sqrt(n);
    for ( int i = 1; i <= n; ++i ) p[i] = ( i - 1 ) / m + 1;//記錄所屬分塊
    for ( int i = 1; i <= n; ++i ) scanf( "%d", &a[i] );
    for ( int i = 1; i <= n; ++i ){
        scanf( "%d%d%d%d", &opt, &l, &r, &c );
        if ( opt == 0 ) Add( l, r, c );
        else printf( "%d\n", v[p[r]] + a[r] );//所屬分塊共同加數+原陣列的值
    }
    return 0;
}

總結

分塊程式碼可以比線段樹簡潔不少,雖然暴力但十分巧妙,而且十分靈活,適用於更多的題目。

但是如果時間複雜度要求較高,分塊的O(n sqrt(n))就不能承受了,所以還是要學會乖乖打線段樹QAQ。

數列分塊系列目錄

數列分塊入門1 <-

數列分塊入門2 還沒呢QAQ

數列分塊入門3 還沒呢QAQ

數列分塊入門4 還沒呢QAQ

數列分塊入門5 還沒呢QAQ

數列分塊入門6 還沒呢QAQ

數列分塊入門7 還沒呢QAQ

數列分塊入門8 還沒呢QAQ

數列分塊入門9 還沒呢QAQ

蒲公英 還沒呢QAQ

公主的朋友 還沒呢QAQ