1. 程式人生 > >模板總結——樹狀陣列

模板總結——樹狀陣列

一維樹狀陣列

對於陣列A[1…n],在O(logn)的時間內完成以下任務:
(1)給A[i]的值加上一個數
(2)求A[1]+A[2]+…+A[i]的和

說明

lowbit(i)表示i在二進位制表示法中最後一個’1‘所在位置的值,如lowbit(12)=4,因為12=1100(2)。
lowbit(i)=i&(-1)。

Tree[i]表示A[i]的前lowbit(i)個數的和,包括A[i]。如Tree[12]=A[12]+A[11]+A[10]+A[9]。

i-=lowbit(i)等價於將i的二進位制的最後一個’1‘減去。i的二進位制中最多有logn個’1‘,所以時間複雜度是O(logn)的。
i+=lowbit(i)等價於將i的二進位制的最後一個’1‘補為’0‘。

當i為0時,i+=lowbit(i)依舊會為0,即死迴圈。所以,插入的數值不能為0。

程式碼

void add(int i,int delta)
{
    while(i<maxn)
    {
        Tree[i]+=delta;
        i+=lowbit(i);
    }
}
int sum(int i)
{
    int ans=0;
    while(i)
    {
        ans+=Tree[i];
        i-=lowbit(i);
    }
}

樹狀陣列實現區間更新、單點查詢

任務:
(1)區間[a,b]的值都加x
(2)求區間具體某個位置的數值
樹狀陣列的優勢在於快速進行單點修改和求字首和。而要實現區間更新和單點查詢,也只能通過轉化成單點修改和求字首和實現。
解決:
用兩次單點修改來實現區間修改:add(a,x)和add(b+1,-x).
用求字首和來實現單點查詢:sum(i).

二維樹狀陣列

任務:
(1)對矩陣中的某個數加上一個整數
(2)查詢某個子矩陣的和

Tree[i][j]表示由從第i行開始的前lowbit(i)行,從第j列開始的前lowbit(j)列組成的子矩陣的和。

求子矩陣第x1行到第x2行,第y1列到第y2列的和:
ans=sum(x2,y2)-sum(x2,y1-1)-(x1-1,y2)+sum(x1-1,y1-1)。

程式碼

void add(int i,int j,int delta)
{
    A[i][j]+=delta;
    for(int x=i;x<A.length;x+=lowbit(x))
        for
(int y=j;y<A[i].length;y+=lowbit[y]) Tree[x][y]+=delta; } int sum(int i,int j) { int ans=0; for(int x=i;x;x-=lowbit(x)) for(int y=j;y;y-=lowbit(y)) ans+=Tree[x][y]; return ans; }