当使用前缀和或者差分数组的时候,一般会遇到O(n2)的时间复杂度,此时我们可以使用树状数组来对时间复杂度进行优化。
树状数组主要是利用树形结构来优化我们前缀和或差分数组的计算复杂度使得O(n)的时间复杂度变为O(logn),使用总的时间复杂度减少到O(nlogn).。
构建树状数组的核心是lowbit:
lowbit(x)即x&(-x),效果为得到x最后一个1的位置。比如3&-3等于1表示3的最后一个1在第1位。
-3表示3的补码也就是取反加一。
总的代码如下:
#include <iostream> using namespace std; const int N = 1000; #define lowbit(x) ((x)&-(x)) int tree[N] = { 0 }; void Update(int x, int d) { while (x <= N) { tree[x] += d; x += lowbit(x); } } int sum(int x) { int ans = 0; while (x > 0) { ans += tree[x]; x -= lowbit(x); } return ans; } void Print_arr(int* arr,int size) { for (int i = 0; i < size; i++) { cout << *(arr + i) << " "; } cout << endl; } void Print_sum_arr(int size) { for (int i = 0; i <= size; i++) { cout << sum(i) << " "; } } int a[11] = { 0,4,5,6,7,8,9,10,11,12,13 }; int main() { for (int i = 1; i <= 10; i++) Update(i, a[i]); //Print_arr(tree, 11); Print_sum_arr(11); cout << sum(8) - sum(4) << endl; return 0; }
(来自<<算法竞赛>>)
对于Update函数的说明:
void Update(int x, int d) {//更新所有在树状数组中与x有关的数据,使用lowbit查找 while (x <= N) { tree[x] += d; x += lowbit(x); } }
举例说明:对第一个元素更新的时候,1的lowbit为1,那么下一个更新的元素为2,2的lowbit为2,下一个更新4,4的lowbit为8更新8以此类推。
Update之和的,tree里面保存的元素对应上图中的a
对于sum函数的说明:
int sum(int x) {//求前缀和 int ans = 0; while (x > 0) { ans += tree[x]; x -= lowbit(x);//由于之前是按照+lowbit(x)进行更新的,现在-lowbit(x)得到的位置就是该位置前缀和需要用到的元素。 } return ans; }
举例说明:对于第8个元素,8的lowbit为8,但由于之前更新1,2,3,4,5,6,7的时候会对8更新,所以tree[8]就是前八个元素之和
举例说明2:对于第7个元素,7的lowbit为1,更新后6的lowbit为2,4的lowbit为3,结束更新。因此前七个元素之和计算公式为tree[7]+tree[6]+tree[4]。
依次分析:tree[4]表示的是:1,2,3更新过以及4本身,因此tree[4]就是前四个元素之和。
tree[6]表示的是5更新加上6本身,因此tree[6]是第5、6个之和
举例说明3:对于第6个元素,6的lowbit为2,那么-lowbit之和变为4,在进行-lowbit为0.因此前6个元素之和为tree[6]+tree[4]
cout << "tree[4]:" << tree[4] << endl; cout << "tree[6]:" << tree[6] << endl; cout << "sum(6):" << sum(6) << endl;
可以看到sum(6)为tree[4]和tree[6]之和
标签:树状,int,lowbit,元素,tree,更新,数组 From: https://www.cnblogs.com/hailanben/p/17281816.html