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