本文介绍了树状数组 BIT(Binary Indexed Tree)相关的基础算法知识。
1. 基础知识
参考链接:https://www.bilibili.com/video/BV1pE41197Qj
1.1 问题引入:
如果给定一个长度为
- 将第
个数加上 - 输出区间
[x,y]
内每个数的和
我们可以对比一下几种方法实现的时间复杂度:
朴素方法:
单点修改:
求区间和:
进行n次操作,时间复杂度最坏是
前缀和方法:
单点修改:
求区间和:
进行n次操作,时间复杂度最坏是
树状数组方法:
单点修改:
求区间和:
进行n次操作,时间复杂度为
,就可以处理非常大规模的数据了
从上面的三个对比可以知道,树状数组在执行需要的大量特殊操作的时候,具有时间复杂度的优势。
1.2 lowbit 操作:
表示非负整数n在二进制表示下,最后一个1及后面的0一起构成的数值
一般有两种写法:
1 | int lowbit(int x){ |
2. 树状数组
2.1 基本概念:
构建下面这样的数组 t
为树状数组:
树状数组 t
有下面的性质:
t[x]
保存以x为根的子树中叶节点值的和t[x]
节点的长度等于lowbit(x)
t[x]
节点的父节点为t[x+lowbit(x)]
整棵树的深度为
2.2 基本操作:
操作1:add(x,k)
即给第
x
个数加上k
。例如:给
A[3]
加上k
。逐级向上都加上k
。
操作2:ask(x)
查询
x
的前缀和。例如:查询前缀和
S[7]
的值,就从t[7]
开始逐级向上加。
2.3 初始化:
已经给定了一个普通的数组 A
,如何将它装进树状数组呢?
方法1: 使用 add
一个个装
最简单的方法,时间复杂度为
1 | for (int i; i <= n; i++) |
方法2: 使用 trick
时间复杂度为
先计算出来A的前缀和 S,再套下面的公式
1 | for( int i =1 ;i <= n ; i++) |
2.4 能应用的问题:
单点修改,区间查询
维护标准的树状数组即可。
单点修改:
- x 位置加上 d,只需要
add(x,d)
即可
区间查询:
查询 [l,r]
的区间和,只需要 ans = ask(r) - ask(l-1)
即可
区间修改,单点查询
引入差分数组 b
,用树状数组维护 b
的前缀和,即 a[]
每个元素的增量。
区间修改:
[l,r]+d
操作,只需要:add(l,d), add(r+1,d)
即可
单点查询:
查询 a[x]
,只需要 ans = a[x] + ask(x)
即可
区间修改,区间查询
3. 例题
P3374 【模板】树状数组 1
Problem:P3374 【模板】树状数组 1
非常标准的单点修改,区间查询例题。
1 |
|
P3368 【模板】树状数组 2
Problem:P3368 【模板】树状数组 2
非常标准的区间修改,单签点查询例题。
1 |
|