树状数组是一个优美小巧的数据结构,在很多时候可以代替线段树。一句话概括就是,凡是树状数组可以解决的问题,线段树都可以解决,反过来线段树可以解决的问题,树状数组不一定能解决。但是树状数组却因为优美小巧实用而极度吸引人~它能够高效地获取数组中连续n个数的和。概括说,树状数组通常用于解决以下问题:数组{a}中的元素可能不断地被修改,怎样才能快速地获取连续几个数的和?
对于维护的序列A
,定义C[i]=A[j+1]+...+A[i]
,其中j
为i
的二进制表示中把最右边的1换成0的值。j
的值可以通过lowbit
求出,即i-lowbit(i)
。
给定序列(数列)A,我们设一个数组C满足
C[i] = A[i–2^k+ 1] + … + A[i]
其中,k为i在二进制下末尾0的个数,i从1开始算!
则我们称C为树状数组。
下面的问题是,给定i,如何求2^k?
答案很简单:2^k=i&(i^(i-1)) ,也就是i&(-i)
图示如下:
当我们修改A[i]的值时,可以从C[i]往根节点一路上溯,调整这条路上的所有C[]即可,这个操作的复杂度在最坏情况下就是树的高度即O(logn)。另外,对于求数列的前n项和,只需找到n以前的所有最大子树,把其根节点的C加起来即可。不难发现,这些子树的数目是n在二进制时1的个数,或者说是把n展开成2的幂方和时的项数,因此,求和操作的复杂度也是O(logn)。
给c语言实现:
int lowbit(int x)
{
return x&(-x);
}
void add(int x,int y)
{
for(int j = x; j <= n; j += lowbit( c[j] += y;
}
int sum(int x)
{
int sum = 0;
for(int j = x; j > 0; j -= lowbit(j))
sum += c[j];
return sum;
}
void qiu(int x,int y)
{
printf("%d\n",sum(y)-sum(x-1));
}
给出一道用树状数组题目链接
http://acm.hdu.edu.cn/showproblem.php?pid=1166http://acm.hdu.edu.cn/showproblem.php?pid=1166
应该可以分分钟ac了~
(对树状数组的原理要好好理解,随后给出lowbit()函数的详解,相信可以帮助理解。。链接来了http://blog.csdn.net/sheldon761642718/article/details/40406407)