树状数组
树状数组是一个查询和修改都为log(n)的数据结构。
主要用于两个数之间的所有元素之和。
树状数组和线段树很像。
但能用树状数组解决的问题,基本上都能用线段树解决,
而线段树能解决的树状数组不一定能解决。
相比较而言,树状数组效率要高很多。
一、回顾一维树状数组
假设一维数组为A[i](i=1,2,...n),则与它对应的树状数组C[i](i=1,2,...n)是这样定义的:
C1 = A1
C2 = A1 + A2
C3 = A3
C4 = A1 + A2 + A3 + A4
C5 = A5
C6 = A5 + A6
C7 = A7
C8 = A1 + A2 + A3 + A4 + A5 + A6 + A7 + A8
……
C16 = A1 + A2 + A3 + A4 + A5 + A6 + A7 + A8 + A9 + A10 + A11 + A12 + A13 + A14 + A15 + A16
......
一个有趣的性质
第C(x)管辖的区域为:2^k(k为二进制末尾的0的个数)
比如:
C(8) 8的二进制为1000 所以 k=3 2^3=8
第一个函数:
int lowbit (int x)//计算C(x)展开的项数
{
return x&-x;
}
X即为数组的下标
意义:计算2^k的值,即C(x)管辖的区域
第二个函数:
<pre class="objc" name="code">int add (int x,int b)
{
while(x<=MAXN)
{
Tree(x)+=b;
x+=lowbit(x);
}
}
解释:
1:b是增量在树的结构中,每次增加b,即为修改了多少,是一个变量。修改了A(x)的值,会一直追溯到最终的父节点。
比如:A(2)增加了b,则该循环使其父节点依次增加b个单位。
C(2) C(4) C(8) 依次增加了b个单位。
2:x是数组的下标。
第三个函数:
int sum (int x)
{
int ret=0;
while(x>0)
{
ret+=tree[x];
x-=lowbit(x);
}
return ret;
}
解释:
1:求出范围a-b的和
Sum(b)-sum(a)即为所求。
求数列A[]的前n项和,只需找到n以前的所有最大子树,把其根节点的C加起来即可。
如:Sun(1)=C[1]=A[1];
Sun(2)=C[2]=A[1]+A[2];
Sun(3)=C[3]+C[2]=A[1]+A[2]+A[3];
Sun(4)=C[4]=A[1]+A[2]+A[3]+A[4];
Sun(5)=C[5]+C[4];
Sun(6)=C[6]+C[4];
Sun(7)=C[7]+C[6]+C[4];
Sun(8)=C[8];
2015.7.21 谢坤