下面是我自己對樹狀數組的認識。。。
樹狀數組,是一個查詢和修改複雜度都爲log(n)的數據結構。樹狀數組支持區間查詢,單值修改。樹狀數組之所以查詢快速,是因爲相當於對數組有效的劃分和維護。
這圖是盜的度孃的。。。C數組就是樹狀數組。
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數組每一個元素都有一個維護長度(也就是管轄範圍),假設元素C[x]維護長度爲l,則元素表示的是從A[x-l+1]到A[x]的所有元素和。因此,樹狀數組的建立主要在於每個元素的維護長度的求解。對於結點編號爲x的結點,假設它的二進制表示形式末尾有t個連續的0,那麼該結點的維護長度就是2^t。也就是說,該元素表示的是從A[x-2^t+1]到A[x]的所有元素和。
而對於給定的x,它的二進制表示形式末尾0的個數t可用一個短小精悍的公式求出:k=(x)&(-x)。 這公式研究不了,直接用了。。。
int lowbit(int x)
{
return x&(-x);
}
所以,可以得出計算前k項元素和的算法。
int sum(int k)
{
int ans = 0;
while(k>0)
{
ans += c[k];
k = k- lowbit(k);
}
return ans;
}
如果要求的不是從頭開始的某段元素的和,可以利用減法。比如求的是A[m]到A[n]之間元素的和,可以S(n)-S(m-1)求出。
而對於單值的更新,要將其關聯的所有C數組中元素的值全部更新。下面對A[t]的值更新。
void add(int t,int value)
{
while(t<=n)
{
c[t] += value;
t = t + lowbit(t);
}
}
最後就是C數組的初始建立了。
void build()
{
for (int i=1;i<=n;i++)
{
c[i]=A[i];
for (int j=i-1; j>i-lowbit(i);j-=lowbit(j))
c[i]+=c[j];
}
}
注意C[i]的管轄範圍。。。
總之,樹狀數組就是通過管轄範圍(下標維護)來提高效率的。
void build()
{
for (int i=1;i<=n;i++)
{
c[i]=A[i];
for (int j=i-1; j>i-lowbit(i);j-=lowbit(j))
c[i]+=c[j];
}
}