一、概念
蓝色A[i]表示输入的数组;绿色C[i]表示对应的树状数组
定义C[i]的值为它所有子结点值和A[i]的总和,存在:
- C[1] =
A[1]
- C[2] = C[1] +
A[2]
= A[1] + A[2]- C[3] =
A[3]
- C[4] = C[2] + C[3] +
A[4]
= A[1] + A[2] + A[3] + A[4]- C[5] =
A[5]
- C[6] = C[5] +
A[6]
= A[5] + A[6]- C[7] =
A[7]
- C[8] = C[4] + C[6] + C[7] +
A[8]
= A[1] + A[2] + A[3] + A[4] + A[5] + A[6] + A[7] + A[8]
结论:
- C[i]中一定包含A[i]的值
- i为奇数时: C[i] = A[i]
- C[i]表示数组A的一段连续区间和
二、性质
1. 区间范围
C[i]表示数组A的一段连续区间和,而右区间一定是i,即C[i]管辖区间的末尾元素是A[i]
下标为i的C[i]所管辖元素的个数为
2
k
2^{k}
2k个(k为i的二进制中最低位到最高位连续0
的长度)
例:
- C[8] : ( 8 ) 10 (8)_{10} (8)10 = ( 1000 ) 2 (1000)_{2} (1000)2,最低位到最高位连续0的长度为3,故k=3,管辖数有 2 3 2^{3} 23个,C[8]是8个数(A[1]-A[8])的和
- C[5] : ( 5 ) 10 (5)_{10} (5)10 = ( 0101 ) 2 (0101)_{2} (0101)2,最低位到最高位连续0的长度为0,故k=0,管辖数有 2 0 2^{0} 20个,C[5]是1个数(A[5])的和
- C[4] : ( 4 ) 10 (4)_{10} (4)10 = ( 0100 ) 2 (0100)_{2} (0100)2,最低位到最高位连续0的长度为2,故k=2,管辖数有 2 2 2^{2} 22个,C[4]是4个数(A[1]-A[4])的和
现在的问题则是:给定i,如何求出 2 k 2^{k} 2k,前辈的经验是: 2 k 2^{k} 2k = i&(-i),这里暂时不讨论是如何得出结果的
实现代码
//根据C[i]的下标i获取管辖范围
int lowbit(int i){
return i&(-i);
}
2. 更新
若更新A[i]的值,则需更新C[i]与C[i]祖先节点的值
例:
- 更新A[1] : 则需更新所有包含A[1]的C[i],即C[1]和其祖先节点C[2],C[4],C[8]
过程:- i = 1; C[1]+=A[1]
- lowbit(1) = 1; 1+lowbit(1) = 2 ; C[2]+=A[1]
- lowbit(2) = 2; 2+lowbit(2) = 4 ; C[4]+=A[1]
- lowbit(4) = 4; 4+lowbit(4) = 8 ; C[8]+=A[1]
- 更新A[6] : 则需更新所有包含A[6]的C[i],即C[6]和其祖先节点C[8]
过程:- i = 6; C[6]+=A[6]
- lowbit(6) = 2; 6+lowbit(6) = 8 ; C[8]+=A[6]
实现代码
/**
*param : x,更新位置
*param : v,增加或减少值
*param : n,数组长度
**/
void update(int x,int v,int n){
a[x] +=v;
for(int i = x; i<=n; i+=lowbit(i)){
c[i] += v;
}
}
3. 区间和
//求A[1]-A[X]的和
int getSum(int x){
int sum = 0;
for(int i = x; i > 0 ;i-=lowbit(i)){
sum += c[i];
}
return sum;
}