感谢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 树状数组入门的更多相关文章

  1. Implementation:Binary Indexed Tree 树状数组

    #include <iostream> #include <cstdlib> using namespace std; class BinaryIndexedTree { pr ...

  2. 树状数组入门 hdu1541 Stars

    树状数组 树状数组(Binary Indexed Tree(B.I.T), Fenwick Tree)是一个查询和修改复杂度都为log(n)的数据结构.主要用于查询任意两位之间的所有元素之和,但是每次 ...

  3. HDU3333 Turing Tree 树状数组+离线处理

    Turing Tree Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total ...

  4. POJ 3321 Apple Tree(树状数组)

                                                              Apple Tree Time Limit: 2000MS   Memory Lim ...

  5. HDU 3333 - Turing Tree (树状数组+离线处理+哈希+贪心)

    题意:给一个数组,每次查询输出区间内不重复数字的和. 这是3xian教主的题. 用前缀和的思想可以轻易求得区间的和,但是对于重复数字这点很难处理.在线很难下手,考虑离线处理. 将所有查询区间从右端点由 ...

  6. poj2299树状数组入门,求逆序对

    今天入门了树状数组 习题链接 https://blog.csdn.net/liuqiyao_01/article/details/26963913 离散化数据:用一个数组来记录每个值在数列中的排名,不 ...

  7. POJ--3321 Apple Tree(树状数组+dfs(序列))

    Apple Tree Time Limit: 2000MS Memory Limit: 65536K Total Submissions: 22613 Accepted: 6875 Descripti ...

  8. gym 100589A queries on the Tree 树状数组 + 分块

    题目传送门 题目大意: 给定一颗根节点为1的树,有两种操作,第一种操作是将与根节点距离为L的节点权值全部加上val,第二个操作是查询以x为根节点的子树的权重. 思路: 思考后发现,以dfs序建立树状数 ...

  9. POJ 3321:Apple Tree 树状数组

    Apple Tree Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 22131   Accepted: 6715 Descr ...

随机推荐

  1. C++指针之间的赋值与转换规则总结

    C++指针之间的赋值与转换规则总结 Note:以下结论不适用于类的成员函数指针,关于类的成员函数指针会单独讨论. 一.任何类型的指针变量均可直接赋值给const void * 任何类型的非const指 ...

  2. python IDLE中如何执行for、while、if等多行语句

    >>> f=open("E:/pythonLearn/140.txt") >>> for line in f.readlines(): prin ...

  3. DFS初级剪枝及心得

    关于DFS心得: 1.利用结构体,记录mark和题目要求的基本属性. 2.用到递归,使用递归时注意要设置出口,即符合要求时return,注意对递归的理解,对于不同情况可能要传递不同的参数,但出口都是一 ...

  4. Git Pro Book

    目录 2nd Edition (2014) Switch to 1st Edition Download Ebook The entire Pro Git book, written by Scott ...

  5. P1297 网线切割

    P1297 网线切割 题目描述 Wonderland居民决定举行一届地区性程序设计大赛.仲裁委员会志愿负责这次赛事并且保证会组织一次有史以来最公正的比赛.为此,所有参赛者的电脑和网络中心会以星状网络连 ...

  6. Go实现try-catch-finally机制

    前言 许多主流语言诸如:Java.Python都实现了try-catch-finally机制,而Go处理错误的方式却与前两种语言不同.关于Go处理异常的方式是好是坏仁者见仁智者见智,笔者还是更喜欢tr ...

  7. tp事务处理

    数据库事务处理见手册 逻辑事务处理 Model->startTrans(); // 开启事务 if(操作失败) { Model->rollback(); // 回滚 }else { Mod ...

  8. 读json文件发生错误,所遇到的坑

    当我们生产者生产json 文件的时候   消费时用JSON读文件时,如下: val values = kafkardd.map(t=>JSON.parseObject(t._2)) 如果发生以下 ...

  9. js valueOf和toString方法

    JavaScript原生提供一个Object对象,所有其他对象都继承自这个对象,Object对象有valueOf和valueOf方法,所以所有JS数据类型都继承了这两种方法.   valueOf:返回 ...

  10. Python+Selenium框架设计篇之-什么是POM

    前面我们介绍了Python中的单元测试框架unittest,以后我们所有的测试类文件,都采用unittest来辅助我们进行debug和脚本开发.搞定了debug机制和确定了unittest来进行创建和 ...