BIT 树状数组 详解 及 例题
(一)树状数组的概念
如果给定一个数组,要你求里面所有数的和,一般都会想到累加。但是当那个数组很大的时候,累加就显得太耗时了,时间复杂度为O(n),并且采用累加的方法还有一个局限,那就是,当修改掉数组中的元素后,仍然要你求数组中某段元素的和,就显得麻烦了。所以我们就要用到树状数组,他的时间复杂度为O(lgn),相比之下就快得多。下面就讲一下什么是树状数组:
一般讲到树状数组都会少不了下面这个图:
下面来分析一下上面那个图看能得出什么规律:
据图可知:c1=a1,c2=a1+a2,c3=a3,c4=a1+a2+a3+a4,c5=a5,c6=a5+a6,c7=a7,c8=a1+a2+a3+a4+a5+a6+a7+a8,c9=a9,c10=a9+a10,c11=a11........c16=a1+a2+a3+a4+a5+.......+a16。
分析上面的几组式子可知,当 i 为奇数时,ci=ai ;当 i 为偶数时,就要看 i 的因子中最多有二的多少次幂,例如,6 的因子中有 2 的一次幂,等于 2 ,所以 c6=a5+a6(由六向前数两个数的和),4 的因子中有 2 的两次幂,等于 4 ,所以 c4=a1+a2+a3+a4(由四向前数四个数的和)。
(一)有公式:cn=a(n-a^k+1)+.........+an(其中 k 为 n 的二进制表示中从右往左数的 0 的个数)。
那么,如何求 a^k 呢?求法如下:
int lowbit(int x) //取x的最低位1,比如4,则返回4,如5,则返回1
{
return x&(-x);
}
lowbit()的返回值就是 2^k 次方的值。
求出来 2^k 之后,数组 c 的值就都出来了,接下来我们要求数组中所有元素的和。
(二)求数组的和的算法如下:
(1)首先,令sum=0,转向第二步;
(2)接下来判断,如果 n>0 的话,就令sum=sum+cn转向第三步,否则的话,终止算法,返回 sum 的值;
(3)n=n - lowbit(n)(将n的二进制表示的最后一个零删掉),回第二步。
代码实现:
int Sum(int i) //求前i项的和
{
int s = ;
//将前i项分段
while(i > )
{
s += sum[i];
i -= lowbit(i); //去掉i的二进制最后一个
}
return s;
}
(三)当数组中的元素有变更时,树状数组就发挥它的优势了,算法如下(修改为给某个节点 i 加上 x ):
(1)当 i<=n 时,执行下一步;否则的话,算法结束;
(2)ci=ci+x ,i=i+lowbit(i)(在 i 的二进制表示的最后加零),返回第一步。
代码实现:
void update(int i, int val) //将第i个元素增加val
{
//i的祖先都要增加val
while(i <= n)
{
sum[i] += val;
i += lowbit(i); //将i的二进制未位补为得到其祖先
}
}
(二)树状数组的应用
以下数组下标均默认从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}.此时具体的操作如应用一
以下数组下标均默认从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:改变更新路径和求和路径
void update(int x, int val)
{
for(int i=x; i>; i-=lowbit(i))
{
sum[i] += val;
}
} int getSum(int x)
{
int s = ;
for(int i=x; i<MAXN; i+=lowbit(i))
{
s += sum[i];
}
return s;
}
应用三 逆序数
假如给你一个数组a[ ] = {2,5,3,4,1},求b[i],b[i] 表示在a[i],a[i+1]...a[n]中(即位置i的右边)小于等于a[i]的数的个数。对此例b[] = {1,3,1,1,0}。 那么该如何去求得b[i]呢?
操作:应用一位置i的左边,应用三是位置i的右边。 然后只需要在应用一的基础上从后往前操作即可
for(int i=n; i>=; i--) { b[i] = getSum(a[i]); //求前a[i]项的和 update(a[i],); //第a[i]个元素+1 }
应用四
假如给你一个数组a[ ] = {2,5,3,4,1},求b[i],b[i] 表示在a[i],a[i+1]...a[n]中(即位置i的右边)大于等于a[i]的数的个数。对此例b[] = {3,0,1,0,0}。 那么该如何去求得b[i]呢?
操作:只需将数组a倒过来编号,即将a转化为 a[]={4,1,3,2,5} 然后利用应用三
二维树状数组
int lowbit(int x)
{
return x&(-x);
} void update(int x, int y, int val) //将 a[x][y] 的值增加val
{
for(int i=x; i<N; i+=lowbit(i))
{
for(int j=y; j<N; j+=lowbit(j))
{
sum[i][j] += val;
}
}
} int getSum(int x, int y) //求以1,1为左上角端点,学校,x,y为右下角端点的矩阵和.
{
int s = ;
for(int i=x; i>; i-=lowbit(i))
{
for(int j=y; j>; j-=lowbit(j))
{
s += sum[i][j];
}
}
return s;
}
(三)例题
基础应用
HDU 1166 敌兵布阵(树状数组)
http://www.cnblogs.com/ws5167/p/3904004.html
HDU 2689 Sort it (树状数组)
http://www.cnblogs.com/ws5167/p/3915614.html
二维树状数组
三维树状数组
树状数组求逆序数
HDU 1394 Minimum Inversion Number ( 树状数组求逆序数 )
DP+树状数组+离散化
HDU 2227 Find the nondecreasing subsequences (DP+树状数组+离散化)
BIT 树状数组 详解 及 例题的更多相关文章
- HDU 1541.Stars-一维树状数组(详解)
树状数组,学长很早之前讲过,最近才重视起来,enmmmm... 树状数组(Binary Indexed Tree(B.I.T), Fenwick Tree)是一个查询和修改复杂度都为log(n)的数据 ...
- HDU4456-Crowd(坐标旋转+二位树状数组+离散化)
转自:http://blog.csdn.net/sdj222555/article/details/10828607 大意就是给出一个矩阵 初始每个位置上的值都为0 然后有两种操作 一种是更改某个位置 ...
- P1972 [SDOI2009]HH的项链[离线+树状数组/主席树/分块/模拟]
题目背景 无 题目描述 HH 有一串由各种漂亮的贝壳组成的项链.HH 相信不同的贝壳会带来好运,所以每次散步完后,他都会随意取出一段贝壳,思考它们所表达的含义.HH 不断地收集新的贝壳,因此,他的项链 ...
- hdu2838树状数组解逆序
离散化和排序后的序号问题搞得我实在是头痛 不过树状数组解逆序和偏序一类问题真的好用 更新:hdu的数据弱的真实,我交上去错的代价也对了.. 下面的代码是错的 /* 每个点的贡献度=权值*在这个点之前的 ...
- Vijos P1066 弱弱的战壕【多解,线段树,暴力,树状数组】
弱弱的战壕 描述 永恒和mx正在玩一个即时战略游戏,名字嘛~~~~~~恕本人记性不好,忘了-_-b. mx在他的基地附近建立了n个战壕,每个战壕都是一个独立的作战单位,射程可以达到无限(“mx不赢定了 ...
- Vijos P1448 校门外的树【多解,线段树,树状数组,括号序列法+暴力优化】
校门外的树 描述 校门外有很多树,有苹果树,香蕉树,有会扔石头的,有可以吃掉补充体力的…… 如今学校决定在某个时刻在某一段种上一种树,保证任一时刻不会出现两段相同种类的树,现有两个操作: K=1,K= ...
- 树状数组例题-数星星,简单题easy,校门外的树2,清点人数
[例1]数星星 天空中有一些星星,这些星星都在不同的位置,每个星星都有个坐标,如果一个星星的左下方(包括正左和正下)有k颗星星,就说这颗星星是k级的. 比如,上图中,星星5是3级的(1,2,4在其左下 ...
- 算法进阶 (LIS变形) 固定长度截取求最长不下降子序列【动态规划】【树状数组】
先学习下LIS最长上升子序列 看了大佬的文章OTZ:最长上升子序列 (LIS) 详解+例题模板 (全),其中包含普通O(n)算法*和以LIS长度及末尾元素成立数组的普通O(nlogn)算法,当然还 ...
- POJ3468--A Simple Problem with Integers--线段树/树状数组 改段求段
题目描述 You have N integers, A1, A2, ... , AN. You need to deal with two kinds of operations. One type ...
随机推荐
- 面向对象Part3
继承: Extends在英文中的翻译是拓展. 但是java允许多重继承. 一条线.单线. ------------------------------------------------------- ...
- [转]python 常用类库!
Python学习 On this page... (hide) 1. 基本安装 2. Python文档 2.1 推荐资源站点 2.2 其他参考资料 2.3 代码示例 3. 常用工具 3.1 Pytho ...
- typeScence
- Android源码阅读 – Zygote
@Dlive 本文档: 使用的Android源码版本为:Android-4.4.3_r1 kitkat (源码下载: http://source.android.com/source/index.ht ...
- 海量日志分析方案--logstash+kibnana+kafka
下图为唯品会在qcon上面公开的日志处理平台架构图.听后觉得有些意思,好像也可以很容易的copy一个,就动手尝试了一下. 目前只对flume===>kafka===>elacsticSea ...
- 安装Java的IDE Eclipse时出现java.net.SocketException,出现错误Installer failed,show.log
ERROR: org.eclipse.equinox.p2.transport.ecf code=1002 Unable to read repository at http://download.e ...
- iOS之小门道道
1.代理方法不执行 很多时候在你代理方法不执行时,小样,你看看你设置代理了吗?
- Jquery知识点梳理
Jquery $代表选择器 JS 选取元素 操作内容 操作属性 操作样式 <div id="aa" style="width:100px; height:100px ...
- Visual Studio高级调试技巧
1. 设置软件断点,运行到目标位置启动调试器 方法①:使用汇编指令(注:x64 c++不支持嵌入汇编) _asm 方法②:编译器提供的方法 __debugbreak(); 方法③:使用windows ...
- 基于Netty与RabbitMQ的消息服务
Netty作为一个高性能的异步网络开发框架,可以作为各种服务的开发框架. 前段时间的一个项目涉及到硬件设备实时数据的采集,采用Netty作为采集服务的实现框架,同时使用RabbitMQ作为采集服务和各 ...