概念
树状数组(Binary Indexed Tree || Fenwick Tree)是一个查询和修改复杂度都为log(n)的数据结构。主要用于查询任意两位之间的所有元素之和。
在本文中,如果C[i + j] 可以表示成 C[i] 与其他C[]的和,则称C[i+j]为C[i]的父节点。如果C[i] 与 C[j] 没有重叠的A[k],则称C[j]为C[i]的子节点(j<i)。
原理
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
首先介绍一个函数 int lowbit(int x);
这个函数的作用是返回2x的二进制形式下末尾零的个数。
比如 (5)10=(101)2,是末尾有0个2,因此返回1。
比如 (8)10=(1000)2,是末尾有3个2,因此返回8。
再介绍lowbit()函数的用途。
x + lowbit(x) 是此节点的最近父节点,比如6的最近父节点是8,7的最近子节点也是8。
x – lowbit(x) 是此节点的最近子节点,比如8的最近子节点是0,6的最近子节点4。
构造数组C(修改数组C)
假设一开始A[3] = 1,现在我们要把A[3]修改为2,因此要把所有包含A[1]的C[i]加一。
那么,哪一些C[i]是包含A[3]的呢。我们可以观察到,A[i]第一次出现是出现在C[i]。
比如A[1]第一次出现在C[1]中,A[2]第一次出现在C[2]中。
因此,我们只要从C[3]开始加一即可,而下一个加一的节点是谁哪一个?是C[3]的最近父节点。
void change(int i, int x) { //i为第几个要修改的元素 //x为修改后与修改前的差值 while (i <= MAX) { c[i] += x; i = i + lowbit(i);//最近父节点 } }
求数组A[i]的和
假设求前i个A[i]的和,那么我们只需要从C[i]开始累加,每次加此节点的最近子节点即可。
注意!
终止条件不能写为 i >= 0,因为lowbit(0) = 0,会陷入死循环
int getsum(int i) { int sum = 0; while (i > 0) { sum += c[i]; i = i - lowbit(i);//最近子节点 } return sum; }
lowbit函数
lowbit函数的具体原理,如果了解二进制数值的有关知识,就很容易理解了,在这就不过多赘述了,只把源代码贴上来。
int lowbit(int x) { return x & (-x); }
思考
往往需要进行区间求和的时候,我们会使用树状数组。但问题往往不是很简单,我们难以看出怎么运用树状数组。做了几道关于树状数组的题后发现,很多题,都需要我们对题意进行变换,将其数据变得具有一定的顺序性,这样方便我们使用树状数组求和。
例如,青出于蓝而胜于蓝 这道题,我们要先记录下每个节点在dfs序列中的位置,然后就可以从1 到 n 依次顺序遍历。
例如 棋子等级 这道题,由于题目给出的Y坐标是递增的,因此数据本身就是具有顺序性的,因此直接可以得出 degree[ i ] = getsum(x)
完整代码
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
#include <cstdio> #include <iostream> #include <stack> #include <algorithm> #include <time.h> #include <stdlib.h> #include <string.h> #define MAX 100000 using namespace std; int n; int a[100]; int c[100]; int lowbit(int x) { return x & (-x); } void change(int i, int x) { //i为第几个要修改的元素 //x为修改后与修改前的差值 while (i <= MAX) { c[i] += x; i = i + lowbit(i);//最近父节点 } } int getsum(int i) { int sum = 0; while (i > 0) { sum += c[i]; i = i - lowbit(i);//最近子节点 } return sum; } int main() { cin >> n; for (int i = 1; i <= n; i++) { int t; cin >> t; change(i, t); } return 0; }