Binary Indexted Tree 树状数组入门
感谢http://www.cnblogs.com/xudong-bupt/p/3484080.html
树状数组(BIT)是能够完成下述操作的数据结构:
给定一初始值全为零的数列a1,a2a,a3...,an
1.给定i,计算a1+a2+...+ai
2.给定i和x,执行ai+=x
BIT的结构:

数组bit[N]
bit[1]=C1=A1;bit[2]=C2=C1+A2;
BIT就是使用数组来维护上面所说的部分和
以1结尾的1,3,5,7长度为1
以1个0结尾的2,6长度为2
以两个0结尾的4长度为4
这样,编号的二进制就能和区间对应起来
通过这个性质,实现上述功能
修改操作 :
修改a[k]后,c数组的哪些元素受到影响?
p1=k肯定受到影响, 设pi的父亲为p(i+1),则
p(i+1)=pi+2^L,L为pi二进制中末尾0的个数
给a[3]增加x,则p1=3, p2=3+20=4, p3=4+22=8, p4=8+23=16>9,因此修改c[3],c[4],c[8]
它所有的后继(祖先)受影响
找后继
求和操作 :
设要求前k项的和,则从最后一项开始 k1=k
k(i+1)=ki-2^s,
s为ki二进制末尾0的个数 k=7,
则k1=7, k2=k1-20=6, k3=k2-21=4, k4=k3- 22=0,
因此前7项和为c[7]+c[6]+c[4]
1) a[]: 保存原始数据的数组。(操作(1)求其中连续多个数的和,操作(2)任意修改其中一个元素)
e[]: 树状数组,其中的任意一个元素e[i]可能是一个或者多个a数组中元素的和。如e[2]=a[1]+a[2]; e[3]=a[3]; e[4]=a[1]+a[2]+a[3]+a[4]。
2) e[i]是几个a数组中的元素的和?
如果数字 i 的二进制表示中末尾有k个连续的0,则e[i]是a数组中2^k个元素的和,则e[i]=a[i-2^k+1]+a[i-2^k+2]+...+a[i-1]+a[i]。
如:4=100(2) e[4]=a[1]+a[2]+a[3]+a[4];
6=110(2) e[6]=a[5]+a[6]
7=111(2) e[7]=a[7]
3) 后继:可以理解为节点的父亲节点。是离它最近的,且编号末位连续0比它多的就是它的父亲,如e[2]是e[1]的后继;e[4]是e[2]的后继。
如e[4] = e[2]+e[3]+a[4] = a[1]+a[2]+a[3]+a[4] ,e[2]、e[3]的后继就是e[4]。
后继主要是用来计算e数组,将当前已经计算出的e[i]添加到他们后继中。
前驱:节点前驱的编号即为比自己小的,最近的,最末连续0比自己多的节点。如e[7]的前驱是e[6],e[6]的前驱是e[4]。
前驱主要是在计算连续和时,避免重复添加元素。
如:Sum(7)=a[1]+...+a[7]=e[7]+e[6]+e[4]。(e[7]的前驱是e[6], e[6]的前驱是e[4])
计算前驱与后继:
lowbit(i) = ( (i-1) ^ i) & i ;
节点e[i]的前驱为 e[ i - lowbit(i) ];
节点e[i]的后继为 e[ i + lowbit(i) ]
核心思想:
(1)树状数组中的每个元素是原数组中一个或者多个连续元素的和。
(2)在进行连续求和操作a[1]+...+a[n]时,只需要将树状数组中某几个元素的和即可。时间复杂度为O(lgn)
(3)在进行修改某个元素a[i]时,只需要修改树状数组中某几个元素的和即可。时间复杂度为O(lgn)
一维树状数组常用的3个函数
int lowbit(int x) //取x的最低位1,比如4,则返回4,如5,则返回1
{
return x&(-x);
} void update(int i, int val) //将第i个元素增加val
{
//i的祖先都要增加val
while(i <= n)
{
sum[i] += val;
i += lowbit(i); //将i的二进制未位补位得到其祖先
}
} int Sum(int i) //求前i项的和
{
int s = ;
//将前i项分段
while(i > )
{
s += sum[i];
i -= lowbit(i); //去掉i的二进制最后一个
}
return s;
}
基本功能
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
char order[];
int a[];
inline int lowbit(int k)
{
return k&-k;
} inline void update(int i,int N,int plus)///界限N,为第i项加上plus,对应包含它的项也要改变
{
while(i<=N)
{
a[i]+=plus;
i+=lowbit(i);///后继改变
}
}
inline int getsum(int k)///求第1到第k项的和
{
int sum=;
while(k)
{
sum+=a[k];
k-=lowbit(k);
}
return sum;
}
int main()
{
int n;
while(~scanf("%d",&n))
{
memset(a,,sizeof(a));
int x;
for(int i=;i<=n;i++)
{
scanf("%d",&x);
update(i,n,x);
}
for(int i=;i<=n;i++)
{
printf("%d ",a[i]);
}
printf("求前5项和:\n");
printf("%d\n",getsum());
printf("求3-6项和:\n");
printf("%d\n",getsum()-getsum()+a[]);
}
return ;
}
以下数组下标均默认从1开始
应用一
假如给你一个数组a[ ] = {2,5,3,4,1},求b[i],b[i] 表示在a[1],a[2]...a[i-1]中(即位置i的左边)小于等于a[i]的数的个数。对此例b[] = {0,1,1,2,0}。 那么该如何去求得b[i]呢?
解法:假如要得到b[4]的值,对于a[4] = 4. 我们 只要得到在a[1],a[2],a[3] 中出现小于等于4的个数,即1,2,3,4的个数,此例即为2. a[1] = 2 < a[4], a[3] = 3 < a[4]. 所以b[4] = 2;其他的以此类推. 求b[i]的值,需要得到在a[1],a[2]....a[i-1]中出现小于等于a[i]的个数,即1,2...a[i]的个数. 相当于求前a[i]项的和,可用到树状数组.
具体操作
for(int i=1; i<=n; i++)
{
b[i] = getSum(a[i]); //求前a[i]项的和
update(a[i],1); //第a[i]个元素+1
}
应用二
假如给你一个数组a[ ] = {2,5,3,4,1},求b[i],b[i] 表示在a[1],a[2]...a[i-1]中(即位置i的左边)大于等于a[i]的数的个数。对此例b[] = {0,0,1,1,4}。 那么该如何去求得b[i]呢?
解法1: 只需要先将数组a倒过来编号,即将a转换为,a[] ={4,1,3,2,5}.此时具体的操作如应用一
解法2:改变更新路径和求和路径
Binary Indexted Tree 树状数组入门的更多相关文章
- Implementation:Binary Indexed Tree 树状数组
#include <iostream> #include <cstdlib> using namespace std; class BinaryIndexedTree { pr ...
- 树状数组入门 hdu1541 Stars
树状数组 树状数组(Binary Indexed Tree(B.I.T), Fenwick Tree)是一个查询和修改复杂度都为log(n)的数据结构.主要用于查询任意两位之间的所有元素之和,但是每次 ...
- HDU3333 Turing Tree 树状数组+离线处理
Turing Tree Time Limit: 6000/3000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total ...
- POJ 3321 Apple Tree(树状数组)
Apple Tree Time Limit: 2000MS Memory Lim ...
- HDU 3333 - Turing Tree (树状数组+离线处理+哈希+贪心)
题意:给一个数组,每次查询输出区间内不重复数字的和. 这是3xian教主的题. 用前缀和的思想可以轻易求得区间的和,但是对于重复数字这点很难处理.在线很难下手,考虑离线处理. 将所有查询区间从右端点由 ...
- poj2299树状数组入门,求逆序对
今天入门了树状数组 习题链接 https://blog.csdn.net/liuqiyao_01/article/details/26963913 离散化数据:用一个数组来记录每个值在数列中的排名,不 ...
- POJ--3321 Apple Tree(树状数组+dfs(序列))
Apple Tree Time Limit: 2000MS Memory Limit: 65536K Total Submissions: 22613 Accepted: 6875 Descripti ...
- gym 100589A queries on the Tree 树状数组 + 分块
题目传送门 题目大意: 给定一颗根节点为1的树,有两种操作,第一种操作是将与根节点距离为L的节点权值全部加上val,第二个操作是查询以x为根节点的子树的权重. 思路: 思考后发现,以dfs序建立树状数 ...
- POJ 3321:Apple Tree 树状数组
Apple Tree Time Limit: 2000MS Memory Limit: 65536K Total Submissions: 22131 Accepted: 6715 Descr ...
随机推荐
- 【笔记】objdump命令的使用
---恢复内容开始--- objdump命令是Linux下的反汇编目标文件或者可执行文件的命令,它还有其他作用,下面以ELF格式可执行文件test为例详细介绍: objdump -f test 显示t ...
- VIM配置IDE
转载于:https://blog.csdn.net/andre617/article/details/53496490#%E8%84%9A%E6%B3%A8 由于YCM需要vim支持python,需要 ...
- 2016 ACM-ICPC Asia China-Final D 二分
题意:一共有N个冰淇淋球,做一个冰淇淋需要K个球,并且由于稳定性,这K个球还必须满足上下相邻的下面比上面大至少两倍.先给出N个球的质量,问最多能做出多少个冰淇淋? 思路:二分答案并对其检验. 检验标准 ...
- 将WebAPI发布到本地服务器与远程服务器
一.本地服务器 参考链接:https://blog.csdn.net/shuai_wy/article/details/69221858 1.要实现 IIS Express域名.IP的访问,关键在于需 ...
- Android面试收集录17 Android进程优先级
在安卓系统中:当系统内存不足时,Android系统将根据进程的优先级选择杀死一些不太重要的进程,优先级低的先杀死.进程优先级从高到低如下. 前台进程 处于正在与用户交互的activity 与前台act ...
- 通过Aspose.Word和ZXING生成复杂的WORD表格
1.前言 这是我之前做的一个项目中要求的功能模块,它的需求是生成一个WORD文档,需要每页一个表格并且表格中需要插入文字.条形码和二维码等信息,页数可控制.具体的效果如下图所示: 可以看到有以下几点是 ...
- MVC WebAPI 的基本使用
1.什么是WebAPI Web API是网络应用程序接口.包含了广泛的功能,网络应用通过API接口,可以实现存储服务.消息服务.计算服务等能力,利用这些能力可以进行开发出强大功能的web应用. 它可以 ...
- 《Cracking the Coding Interview》——第8章:面向对象设计——题目10
2014-04-24 00:05 题目:用拉链法设计一个哈希表. 解法:一个简单的哈希表,就看成一个数组就好了,每个元素是一个桶,用来放入元素.当有多个元素落入同一个桶的时候,就用链表把它们连起来.由 ...
- 常见算法用Pascal实现
基本算法 这些都是非常基本的的算法,希望所有学习的人都能理解! 1.数论算法 求两数的最大公约数 function gcd(a,b:integer):integ ...
- asp.net 身份验证-Form 身份验证
一. .net身份验证简介 1.身份验证就是检测用户是否登录及所访问的资源是否有权限.当我们在访问一个受保护网络资源时,往往需要输入用户名.密码信息,或通过其他证书.第三方身份验证等方式.验证(Aut ...