树状数组
来源:互联网 发布:js设置div不可见 编辑:程序博客网 时间:2024/04/30 05:36
树状数组在区间求和问题上有大用,其三种复杂度都比线段树要低很多……有关区间求和的问题主要有以下三个模型(以下设A[1..N]为一个长为N的序列,初始值为全0):
(1)“改点求段”型,即对于序列A有以下操作:
【1】修改操作:将A[x]的值加上c;
【2】求和操作:求此时A[l..r]的和。
这是最容易的模型,不需要任何辅助数组。树状数组中从x开始不断减lowbit(x)(即x&(-x))可以得到整个[1..x]的和,而从x开始不断加lowbit(x)则可以得到x的所有前趋。代码:
{
for (int i=x; i<=n; i+=i&(-i)) a[i] += c;
}
int SUM(int x)
{
int s = 0;
for (int i=x; i>0; i-=i&(-i)) s += a[i];
return s;
}
操作【1】:ADD(x, c);
操作【2】:SUM(r)-SUM(l-1)。
(2)“改段求点”型,即对于序列A有以下操作:
【1】修改操作:将A[l..r]之间的全部元素值加上c;
【2】求和操作:求此时A[x]的值。
这个模型中需要设置一个辅助数组B:B[i]表示A[1..i]到目前为止共被整体加了多少(或者可以说成,到目前为止的所有ADD(i, c)操作中c的总和)。
这样就把该模型转化成了“改点求段”型,只是有一点不同的是,SUM(x)不是求B[1..x]的和而是求B[x..N]的和,此时只需把ADD和SUM中的增减次序对调即可(模型1中是ADD加SUM减,这里是ADD减SUM加)。代码:
{
for (int i=x; i>0; i-=i&(-i)) b[i] += c;
}
int SUM(int x)
{
int s = 0;
for (int i=x; i<=n; i+=i&(-i)) s += b[i];
return s;
}
操作【1】:ADD(l-1, -c); ADD(r, c);
操作【2】:SUM(x)。
(3)“改段求段”型,即对于序列A有以下操作:
【1】修改操作:将A[l..r]之间的全部元素值加上c;
【2】求和操作:求此时A[l..r]的和。
这是最复杂的模型,需要两个辅助数组:B[i]表示A[1..i]到目前为止共被整体加了多少(和模型2中的一样),C[i]表示A[1..i]到目前为止共被整体加了多少的总和(或者说,C[i]=B[i]*i)。
对于ADD(x, c),只要将B[x]加上c,同时C[x]加上c*x即可(根据C[x]和B[x]间的关系可得);
而ADD(x, c)操作是这样影响A[1..i]的和的:若x<i,则会将A[1..i]的和加上x*c,否则(x>=i)会将A[1..i]的和加上i*c。也就是,A[1..i]之和 = B[i..N]之和 * i + C[1..i-1]之和。
这样对于B和C两个数组而言就变成了“改点求段”(不过B是求后缀和而C是求前缀和)。
另外,该模型中需要特别注意越界问题,即x=0时不能执行SUM_B操作和ADD_C操作!代码:
{
for (int i=x; i>0; i-=i&(-i)) B[i] += c;
}
void ADD_C(int x, int c)
{
for (int i=x; i<=n; i+=i&(-i)) C[i] += x * c;
}
int SUM_B(int x)
{
int s = 0;
for (int i=x; i<=n; i+=i&(-i)) s += B[i];
return s;
}
int SUM_C(int x)
{
int s = 0;
for (int i=x; i>0; i-=i&(-i)) s += C[i];
return s;
}
inline int SUM(int x)
{
if (x) return SUM_B(x) * x + SUM_C(x - 1); else return 0;
}
操作【1】:
ADD_B(r, c); ADD_C(r, c);
if (l > 1) {ADD_B(l - 1, -c); ADD_C(l - 1, -c);}
操作【2】:SUM(r) - SUM(l - 1)。
来自于http://www.cppblog.com/MatoNo1/archive/2011/03/19/142226.aspx
1、概述
树状数组(binary indexed tree),是一种设计新颖的数组结构,它能够高效地获取数组中连续n个数的和。概括说,树状数组通常用于解决以下问题:数组{a}中的元素可能不断地被修改,怎样才能快速地获取连续几个数的和?
2、树状数组基本操作
传统数组(共n个元素)的元素修改和连续元素求和的复杂度分别为O(1)和O(n)。树状数组通过将线性结构转换成伪树状结构(线性结构只能逐个扫描元素,而树状结构可以实现跳跃式扫描),使得修改和求和复杂度均为O(lgn),大大提高了整体效率。
给定序列(数列)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)
下面进行解释:
以i=6为例(注意:a_x表示数字a是x进制表示形式):
(i)_10 = (0110)_2
(i-1)_10=(0101)_2
i xor (i-1) =(0011)_2
i and (i xor (i-1)) =(0010)_2
2^k = 2
C[6] = C[6-2+1]+…+A[6]=A[5]+A[6]
数组C的具体含义如下图所示:
当我们修改A[i]的值时,可以从C[i]往根节点一路上溯,调整这条路上的所有C[]即可,这个操作的复杂度在最坏情况下就是树的高度即O(logn)。另外,对于求数列的前n项和,只需找到n以前的所有最大子树,把其根节点的C加起来即可。不难发现,这些子树的数目是n在二进制时1的个数,或者说是把n展开成2的幂方和时的项数,因此,求和操作的复杂度也是O(logn)。
树状数组能快速求任意区间的和:A[i] + A[i+1] + … + A[j],设sum(k) = A[1]+A[2]+…+A[k],则A[i] + A[i+1] + … + A[j] = sum(j)-sum(i-1)。
下面给出树状数组的C语言实现:
//求2^k
int
lowbit(
int
t)
{
return
t & ( t ^ ( t - 1 ) );
}
//求前n项和
int
sum(
int
end)
{
int
sum = 0;
while
(end > 0)
{
sum += in[end];
end -= lowbit(end);
}
return
sum;
}
//增加某个元素的大小
void
plus(
int
pos,
int
num)
{
while
(pos <= n)
{
in[pos] += num;
pos += lowbit(pos);
}
}
3、扩展——二维树状数组
一维树状数组很容易扩展到二维,二维树状数组如下所示:
C[x][y] = sum(A[i][j])
其中,x-lowbit[x]+1 <= i<=x且y-lowbit[y]+1 <= j <=y
4、应用
(1) 一维树状数组:
参见:http://hi.baidu.com/lilu03555/blog/item/4118f04429739580b3b7dc74.html
(2) 二维树状数组:
一个由数字构成的大矩阵,能进行两种操作
1) 对矩阵里的某个数加上一个整数(可正可负)
2) 查询某个子矩阵里所有数字的和
要求对每次查询,输出结果
5、总结
树状数组最初是在设计压缩算法时发现的(见参考资料1),现在也会经常用语维护子序列和。它与线段树(具体见:数据结构之线段树)比较在思想上类似,比线段树节省空间且编程复杂度低,但使用范围比线段树小(如查询每个区间最小值问题)。
6、参考资料
(1) Binary Indexed Trees:
http://www.topcoder.com/tc?module=Static&d1=tutorials&d2=binaryIndexedTrees
(2) 吴豪文章《树状数组》:
http://www.java3z.com/cwbwebhome/article/article19/zip/treearray.zip
(3) 郭炜文章《线段树和树状数组》:
http://poj.org/summerschool/1_interval_tree.pdf
来自于http://dongxicheng.org/structure/binary_indexed_tree/- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- POJ 3744Scout YYF I 概率DP+矩阵优化
- Iterator 迭代器
- Java基础知识(上)
- Android官方命令深入分析之虚拟机
- (2) Tomcat 简述
- 树状数组
- hdu3339 In Action 最短路+01背包
- 二叉树左右子树的交换
- strust2文件上传
- HDU 4986 Little Pony and Alohomora Part I(递推+欧拉常数)
- HDOJ Intersection 5120【环相交面积】
- HDUOj 简单计算器
- hdu 1999 不可摸数(筛法求数n的真因子之和)
- PHP---运行时出现waring如何取消