二維樹狀數組——SuperBrother打鼴鼠(Vijos1512)

  樹狀數組(BIT)是一個查詢和修改複雜度都爲log(n)的數據結構,主要用於查詢任意兩位之間的全部元素之和,其編程簡單,很容易被實現。並且能夠很容易地擴展到二維。讓咱們來看一道很裸的二維樹狀數組題:編程

    在一個「打鼴鼠」的遊戲中,鼴鼠會不時地從洞中鑽出來,不過不會從洞口鑽進去(鼴鼠真膽大……)。洞口都在一個大小爲n(n<=1024)的正方形中。這個正方形在一個平面直角座標系中,左下角爲(0,0),右上角爲(n-1,n-1)。洞口所在的位置都是整點,就是橫縱座標都爲整數的點。而SuperBrother也不時地會想知道某一個範圍的鼴鼠總數。這就是你的任務。數組

  每一個輸入文件有多行。
    第一行,一個數n,表示鼴鼠的範圍。
    之後每一行開頭都有一個數m,表示不一樣的操做:
      m=1,那麼後面跟着3個數x,y,k(0<=x,y<n),表示在點(x,y)處新出現了k只鼴鼠;
      m=2,那麼後面跟着4個數x1,y1,x2,y2(0<=x1<=x2<n,0<=y1<=y2<n),表示詢問矩形(x1,y1)-(x2,y2)內的鼴鼠數量;
      m=3,表示老師來了,不能玩了。保證這個數會在輸入的最後一行。數據結構

    詢問數不會超過10000,鼴鼠數不會超過maxlongint。函數

  把這個問題簡單抽象一下。就是:spa

  • 輸入1時,對一個二維矩陣中某個位置上加上一個數k。
  • 輸入2時,求出矩陣中[(x1,y1);(x2,y2)]的子矩陣中,元素的總大小並輸出。
  • 輸入3時,退出。

  這個問題,輸入數據規模可能很大(雖然實際上並不是這樣,出題人懶到隨機生成數據)。並且是動態修改,顯然不能使用前綴和,因此,樹狀數組就是咱們的首選。而然,樹狀數組原本是一維的,如何把它推廣到二維去呢。其實很簡單,其方法相似與先生成沒一行原數組的一維樹狀數組,再把一個個一維樹狀數組組合成二維的其對應關係爲:code

  C[1][1]=a[1][1],C[1][2]=a[1][1]+a[1][2],C[1][3]=a[1][3],C[1][4]=a[1][1]+a[1][2]+a[1][3]+a[1][4],c[1][5]=a[1][5],C[1][6]=a[1][5]+a[1][6],... blog

  C[2][1]=a[1][1]+a[2][1],C[2][2]=a[1][1]+a[1][2]+a[2][1]+a[2][2],C[2][3]=a[1][3]+a[2][3],C[2][4]=a[1][1]+a[1][2]+a[1][3]+a[1][4]+a[2][1]+a[2][2]+a[2][3]+a[2][4], C[2][5]=a[1][5]+a[2][5],C[2][6]=a[1][5]+a[1][6]+a[2][5]+a[2][6],...遊戲

  C[3][1]=a[3][1],C[3][2]=a[3][1]+a[3][2],C[3][3]=a[3][3],C[3][4]=a[3][1]+a[3][2]+a[3][3]+a[3][4],C[3][5]=a[3][5],C[3][6]=a[3][5]+a[3][6],... it

  C[4][1]=a[1][1]+a[2][1]+a[3][1]+a[4][1],C[4][2]=a[1][1]+a[1][2]+a[2][1]+a[2][2]+a[3][1]+a[3][2]+a[4][1]+a[4][2],C[4][3]=a[1][3]+a[2][3]+a[3][3]+a[4][3],...(太多了,我就寫到3吧) io

  ……

  經過觀察能發現,第一行是自己,第二行是第一行加上其自己,第三行是自己,第四行是第1、二行加上其自己。這和一維的樹狀數組是一摸同樣的。因此,咱們很容易就能夠寫出修改、查詢 函數。

void modfily(int x,int y,int data){
    x+=1;y+=1;
    for (int i=x;i<=n;i+=lowbit(i))
        for (int j=y;j<=n;j+=lowbit(j))
            c[i][j]+=data;
}
int sum(int x,int y){
    x+=1;y+=1;
    int result=0;
    for (int i=x;i>0;i-=lowbit(i))
        for (int j=y;j>0;j-=lowbit(j))
            result+=c[i][j];
    return result;
}

  這就是二維樹狀數組的核心了,代碼和一維的相仿,異常簡單。你們可能有疑問,爲何i,j要加1。其實這是我被坑之後的領悟,若是i,j=0的話,lowbit也永遠爲0,程序會陷入死循環,直接TLE。從這兩個函數咱們也能夠看出,二維樹狀數組的查詢、修改時間複雜度爲log(n)²。

  數據結構部分解決了,怎麼求一個子矩陣中的數的和呢?畫張圖咱們就能夠求出,公式爲:sum(x2, y2) - sum(x1-1, y2) - sum(x2, y1-1) + sum(x1-1, y1-1)

  順便附上這道例題的AC code:

#include <cstdio>
#include <cstdlib>
using namespace std;

int m=0,n,x,y,k,x1,y1,c[1026][1026];
void modfily(int,int,int);
int sum(int,int);
inline int lowbit(int x){
    return x&(-x);
}

int main(void){
    scanf("%d",&n);
    for (int i=0;i<n;++i)
        for (int j=0;j<n;++j)   c[i][j]=0;
    while (m!=3){
        scanf("%d",&m);
        if (m==1){
            scanf("%d%d%d",&x,&y,&k);
            modfily(x,y,k);
        }
        if (m==2){
            scanf("%d%d%d%d",&x,&y,&x1,&y1);
            printf("%d\n",abs(sum(x1,y1)-sum(x-1,y1)-sum(x1,y-1)+sum(x-1,y-1)));
        }
    }
    return 0;
}
void modfily(int x,int y,int data){
    x+=1;y+=1;
    for (int i=x;i<=n;i+=lowbit(i))
        for (int j=y;j<=n;j+=lowbit(j))
            c[i][j]+=data;
}
int sum(int x,int y){
    x+=1;y+=1;
    int result=0;
    for (int i=x;i>0;i-=lowbit(i))
        for (int j=y;j>0;j-=lowbit(j))
            result+=c[i][j];
    return result;
}
相關文章
相關標籤/搜索