int lowbit(int t)
{
return t&(-t);
}
void add(int x,int y)
{
for(int i=x;i<=n;i+=lowbit(i))
tree[i]+=y;
}
int getsum(int x)
{
int ans=0;
for(int i=x;i>0;i-=lowbit(i))
ans+=tree[i];
return ans;
}
這篇筆記 會詳細的講解,使得隊員們對樹狀數組
完全入門 而不是懵懵懂懂。
以上先給出 最多見的,三個函數。(單點更新,區間查詢)
網上的解釋以及分析有不少,這裏是個人一點總結和體會概括一下,而且在週三(2016.12.07)的講座以後會發布在團隊筆記中,
請隊員們細細閱讀,而且
補題。
下面開始
*************************************************分割線
樹狀數組 重點是在
樹狀的數組
你們都知道二叉樹吧
葉子結點表明A數組A[1]~A[8]
.......
如今變形一下
如今定義每一列的頂端結點C[]數組
以下圖
C[i]表明 子樹的葉子結點的權值之和//
這裏以求和舉例
如圖能夠知道
C[1]=A[1];
C[2]=A[1]+
A[2]
;
C[3]=A[3];
C[4]=
A[1]+
A[2]+A[3]+A[4]
;
C[5]=A[5];
C[6]=A[5]+A[6];
C[7]=A[7];
C[8]=
A[1]+
A[2]+A[3]+A[4]+A[5]+A[6]+A[7]+A[8]
;
下面觀察以下圖
將C[]數組的結點序號轉化爲
二進制
1=(001)
C[1]=A[1];
2=(010)
C[2]=A[1]+
A[2]
;
3=(011)
C[3]=A[3];
4=(100)
C[4]=
A[1]+
A[2]+A[3]+A[4]
;
5=(101)
C[5]=A[5];
6=(110)
C[6]=A[5]+A[6];
7=(111)
C[7]=A[7];
8=(1000)
C[8]=
A[1]+
A[2]+A[3]+A[4]+A[5]+A[6]+A[7]+A[8]
;
對照式子能夠發現 C[i]=A[i-2^k+1]+A[i-2^k+2]+......A[i]; (k爲i的二進制中從最低位到高位連續零的長度)例如i=8時,k=3;
能夠自行帶入驗證;
如今引入lowbit(x)
lowbit(x) 其實就是取出x的最低位1 換言之 lowbit(x)=2^k k的含義與上面相同 理解一下
int lowbit(int t)
{
return t&(-t);
}
//-t 表明t的負數 計算機中負數使用對應的正數的補碼來表示
//例如 :
// t=6(0110) 此時 k=1
//-t=-6=(1001+1)=(1010)
// t&(-t)=(0010)=2=2^1
C[i]=A[i-2^k+1]+A[i-2^k+2]+......A[i];
C[i]=
A[i-lowbit(i)+1]+A[i-lowbit(i)+2]+......A[i];
*************************************************分割線
區間查詢
ok 下面利用C[i]數組,求A數組中前i項的和
舉個例子 i=7;
sum[7]=A[1]+A[2]+A[3]+A[4]+A[5]+A[6]+A[7] ; 前i項和
C[4]=
A[1]+
A[2]+A[3]+A[4]
;
C[6]=A[5]+A[6];
C[7]=A[7];
能夠推出:
sum[7]=C[4]+C[6]+C[7];
序號寫爲二進制: sum[(111)]=
C[(100)]+C[(110)]+C[(111)];
再舉個例子 i=5
sum[7]=A[1]+A[2]+A[3]+A[4]+A[5] ; 前i項和
C[4]=
A[1]+
A[2]+A[3]+A[4]
; C[5]=A[5];
能夠推出:
sum[5]=C[4]+C[5];
序號寫爲二進制: sum[(101)]=
C[(100)]+C[(101)];
細細觀察二進制 樹狀數組追其根本就是二進制的應用
結合代碼
int getsum(int x)
{
int ans=0;
for(int i=x;i>0;i-=lowbit(i))
ans+=C[i];
return ans;
}
對於i=7 進行演示
7(111)
ans+=C[7]
lowbit(7)=001 7-
lowbit(7)=6(110) ans+=C[6]
lowbit(6)=010 6-lowbit(6)=4(100) ans+=C[4]
lowbit(4)=100 4-lowbit(4)=0(000)
對於i=5 進行演示
5(101)
ans+=C[5]
lowbit(5)=001 5-
lowbit(5)=4(100) ans+=C[4]
lowbit(4)=100 4-lowbit(4)=0(000)
*************************************************分割線
單點更新
當咱們修改A[]數組中的某一個值時 應當如何更新C[]數組呢?
回想一下 區間查詢的過程,再看一下上文中列出的圖
結合代碼分析
void add(int x,int y)
{
for(int i=x;i<=n;i+=lowbit(i))
tree[i]+=y;
}
//能夠發現 更新過程是查詢過程的逆過程
//由葉子結點向上更新C[]數組
如圖:
當更新A[1]時 須要向上更新C[1] ,C[2],C[4],C[8]
C[1], C[2], C[4], C[8]
寫爲二進制
C[(001)],C[(010)],C[(100)],C[(1000)]
1(001) C[1]+=A[1]
lowbit(1)=001 1+lowbit(1)=2(010)
C[2]+=A[1]
lowbit(2)=010 2+lowbit(2)=4(100) C[4]
+=A[1]
lowbit(4)=100 4+lowbit(4)=8(1000) C[8]
+=A[1]
*************************************************分割線
先這樣
講解題目: