平常我们经常会遇到一些对数组进行维护查询的操作,比较常见的如:修改某点的值,求某个区间的和。当数据规模不大的时候可以使用朴素方法,但当规模增大后是划不来的。而这两个操作正是树状数组的强项。
树状数组的结构如图所示:
从图中可以看出:c[1] = a[1]
c[2] = a[1] + a[2]
c[3] = a[3]
c[4] = a[1] + a[2] + a[3] + a[4] 等等
树状数组这样的结构,其元素之间的下标就存在一定的对应关系:
int lowbit(int x)
{
return x&(-x);
}
通过lowbit来求出某个结点管辖范围,如果x+=x&(-x),就得到该节点的父节点的下标值,如x=4时,就得到8;而x-=x&(-x),就是得到该节点的管辖区间的下个区间的管辖点,如x=7,代入后6,不断循环到0.
修改某点值的函数:
void update(int x,int num)
{
while(x<=N)
{
d[x]+=num;
x+=lowbit(x);
}
}
对于一般的数组只需要修改点的值即可,然而树状数组存储的其左右子节点的和,所以需要修改所有该点被管辖的区间。比如a[2]减1,所有2被管辖的点4,8,16都应该减1
区间求和函数:
int getSum(int x)
{
int s=0;
while(x>0)
{
s+=d[x];
x-=lowbit(x);
}
return s;
}
两种情况:
一。每次修改的是一个点,所求的是关于某段区间:向上修改,向下求和
二。每次修改的是一个区间,所求的值关于某个点的:要向下修改,将它后面的区间都加一遍,再向后修改,把不必要的修改区间再减去,用他的父节点记录每个点的染色次数。
void update(int x,int num)
{
while(x>0)
{
d[x]+=num;
x-=lowbit(x);
}
}
int getSum(int x)
{
int s=0;
while(x<=N)
{
s+=d[x];
x+=lowbit(x);
}
return s;
}
练习题:POJ 2352 POJ 2155 POJ 2299 POJ3067 POJ 2352 POJ 3321 POJ2309 POJ1990
http://www.cnblogs.com/Penn000/articles/5758324.html
https://www.topcoder.com/community/data-science/data-science-tutorials/binary-indexed-trees/#prob