树状数组的核心在于对一段区间的修改和查询,巧妙地把数组用一颗树表示,从而降低了对数组的修改和查询的复杂度。
首先引入lowbit()
int lowbit(x) { return x & (-x); }
如:
x =1: 1 &-1
0000 0001 & 1111 1111 = 1
x = 6: 6 & -6
0000 0110 & 1111 1010 = 2
总结一下就是:
求出某个数二进制表示从右起出现的第一个1及后面的0一起表示的二进制数,如6的二进制表示为110,向左数第一个为0,第二个为1,故Lowbit(6) = 10(2) = 2(10)。
现在看树状数组的图:
我们可以发现没个子节点的父亲节点的下标就是该节点的下标 i + lowbit(i),利用这个特性,我们就可以得到对树状数组的两个操作函数
更新节点:
void Add(int x, int y) { for(int i = x;i <= n;i += lowbit(i)) { c[i] += y; } }
查询区间:
int Sum(int x) { int ans = 0; for(int i = x;i > 0;i -= lowbit(i)) { ans += c[i]; } return ans; }
用这两个函数就可以对树状数组进行更新和查询操作。