根据数组[1, 2, 3, 4, 5]来创建对应的树状数组
**树状数组**或**二元索引树**（英語：），又以其发明者命名为Fenwick树，最早由Peter M. Fenwick于1994年以A New Data Structure for Cumulative Frequency Tables为题发表在SOFTWARE PRACTICE AND EXPERIENCE。其初衷是解决数据压缩裡的累积频率（Cumulative Frequency）的计算问题，现多用于高效计算数列的前缀和， 区间和。它可以以O(\log n)的时间得到任意前缀和{\displaystyle \sum \_{i=1}^{j}A[i],1<=j<=N}，并同时支持在O(\log n)时间内支持动态单点值的修改。空间复杂度O(n)。

结构起源
----

按照Peter M. Fenwick的说法，正如所有的整数都可以表示成2的幂和，我们也可以把一串序列表示成一系列子序列的和。采用这个想法，我们可将一个前缀和划分成多个子序列的和，而划分的方法与数的2的幂和具有极其相似的方式。一方面，子序列的个数是其二进制表示中1的个数，另一方面，子序列代表的f[i]的个数也是2的幂。

基本操作
----

### 预备函数

定义一个`lowbit`函数，返回参数转为二进制后,最后一个1的位置所代表的数值。

例如，`lowbit(34)`的返回值将是2；而`lowbit(12)`返回4；`lowbit(8)`返回8。

将34转为二进制为(0010 0010)2。这里的"最后一个1"指的是从2^{0}位往前数，见到的第一个1，也就是2^{1}位上的1。

程序上，`(~i + 1) & i`表明了最后一位1的值。

仍然以34为例，~(0010 0010)的结果是1101 1101（221），加1后为1101 1110（222），把0010 0010与1101 1110作AND，得0000 0010（2）。

`lowbit`的一个简便求法:（C++）

```
int lowbit(int x)
{
 return x&(-x);
}

```

### 新建

定义一个数组 BIT，用以维护A的前缀和，则：{\displaystyle BIT\_{i}=\sum \_{j=i-lowbit(i)+1}^{i}A\_{j}}

具体能用以下方式实现：（C++）

```
void build()
{ 
 for (int i = 1; i <= MAX\_N; i++)
 {
 BIT[i] = A[i - 1];
 for (int j = i - 2; j >= i - lowbit(i); j--)
 BIT[i] += A[j];
 }
}
//注：这里的求和将汇集到非终端结点（D00形式）
//BIT中仅非终端结点i值是 第0~i元素的和
//终端结点位置的元素和，将在求和函数中求得
//BIT中的index，比原数组中大1

```

### 修改

假设现在要将A[i]的值增加delta，

那么，需要将BIT[i]覆盖的区间包含A[i]的值都加上delta，

这个过程可以写成递归，或者普通的循环。

需要计算的次数与数据规模N的二进制位数有关，即这部分的时间复杂度是{\displaystyle O(\log {N})}。

修改函数的C++写法：

```
void edit(int i, int delta)
{
 for (int j = i; j <= MAX\_N; j += lowbit(j))
 BIT[j] += delta;
}

```

### 求和

假设我们需要计算\sum \_{{i=1}}^{{k}}A\_{i}的值。

1. 首先，将ans初始化为0，将i初始化为k。
2. 将ans的值加上`BIT[i]`。
3. 将i的值减去`lowbit(i)`。
4. 重复步骤2～3，直到i的值变为0。

求和函数的C/C++写法：

```
int sum (int k)
{
 int ans = 0;
 for (int i = k; i > 0; i -= lowbit(i))
 ans += BIT[i];
 return ans;
}

```

### 时空复杂度

* 初始化复杂度最优为：O(N)
* 单次询问复杂度：O(\log N)，其中N为数组大小
* 单次修改复杂度：O(\log N)，其中N为数组大小
* 空间复杂度：O(N)

### 扩展

树状数组可以通过维护差分数组来处理区间修改和单点查询。具体地，当我们在 {\displaystyle [l,r]} 增加 d 时只需执行 `edit(l,d)` 和 `edit(r+1,-d)` 。查询则与单点修改区间查询相似。

应用
--

### 求逆序对数

逆序对数是一个数列中在它前面有比它大的个数。如4312的逆序对数是0+1+2+2=5。

可以先把数列中的数按大小顺序转化成1到n的整数，使得原数列成为一个{\displaystyle 1,2,...n}的排列P，创建一个树状数组，用来记录这样一个数组A（下标从1算起）的前缀和：若排列中的数i当前已经出现，则A[i]的值为1，否则为{\displaystyle 0}。初始时数组A的值均为{\displaystyle 0}，从排列中的最後一个数开始遍历，每次在树状数组中查询有多少个数小于当前的数{\displaystyle P[j]}（即用树状数组查询数组A目前{\displaystyle P[j]-1}个数的前缀和）并加入计数器，之后对树状数组执行修改数组A第{\displaystyle P[j]}个数值加1的操作。

参考文献
----

1. ↑  Peter M. Fenwick.. Software: Practice and Experience. 1994, **24** (3): 327–336  [2015-10-24]. doi:10.1002/spe.4380240306. （原始内容存档于2021-02-25）.
2. ↑ .  [2012-05-09]. （原始内容存档于2019-08-23）.
3. ↑ .  [2012-05-09]. （原始内容存档于2016-06-16）.
4. ↑ .  [2012-11-18]. （原始内容存档于2013-04-10）.
5. ↑ .  [2012-05-11]. （原始内容存档于2019-11-30）.

|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| 

| * 
* 论
* 编
计算机科学中的树 |
| --- |
|  |
| 二叉树 | 
* 二元搜尋樹
* 笛卡尔树
* MVP树
* Top tree（英语：）
* T树
* 线索二叉树

 |
|  |
| 自平衡二叉查找树 | 
* AA树
* AVL树
* 左倾红黑树
* 红黑树
* 替罪羊树
* 伸展树
* 树堆
* 加权平衡树

 |
|  |
| B树 | 
* B+树
* B\*树
* Bx树
* UB树
* 2-3树
* 2-3-4树
* (a,b)-树
* 跳舞樹（英语：）
* H树

 |
|  |
| 堆 | 
* 二叉堆
* 二项堆
* 斐波那契堆
* 左偏树
* 配对堆
* 斜堆
* 范恩德蟒蛇樹（英语：）

 |
|  |
| Trie | 
* 后缀树
* 基数树
* 三叉查找树
* X-快速前缀树
* Y-快速前缀树
* AC自动机

 |
|  |
| 二叉空间分割（BSP）树 | 
* 四叉树
* 八叉树
* *k*-d树
* 隐式*k*-d树
* VP树

 |
|  |
| 非二叉树 | 
* 指数树（英语：）
* 融合树（英语：）
* PQ树（英语：）
* SPQR树（英语：）

 |
|  |
| 空间数据分割树 | 
* R树
* R\*树
* R+树
* X树
* M树
* 線段樹 (儲存區間)
* 线段树 (区间查询)
	+ 可持久化线段树
* 希尔伯特R树
* 优先R树

 |
|  |
| 其他树 | 
* 散列日历
* 散列树
* 手指樹（英语：）
* 顺序统计树
* 度量樹（英语：）
* 覆蓋樹（英语：）
* BK树
* 二重連鎖樹（英语：）
* iDistance（英语：）
* Link-cut tree（英语：）
* Log-structured merge-tree（英语：）
* 树状数组
* 哈希树

 |

 |