树状数组本质上就是一个一维数组,那么我们为什么说它是树状,原因就在于其逻辑结构。树状数组主要实现两个功能:一是对某个元素的修改,二是求子数列连续和。
比如现在有这样的一列数a[1]~a[16]:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16。我们开辟一个树状数组tr[N],用来辅助求出数组a的前n项和。最朴素求前n项和的方法就是依次遍历a[i] += a[i-1],时间复杂度是O(n),我们利用树状数组便可以在O(logn)的时间复杂度求出a的前n项和。
树状数组的存储方式(第几级指的是该数二进制形式从右往左数第一个1是第几位 比如001:第一级;010:第二级……):比如现在我们读入a[1]这个数字,也就是1,毫无疑问此时tr[1]也应当存的是1,(树状数组与一般数组存储的不同之处:)此时tr[1](第一级) tr[2](第二级) tr[4](第三级) tr[8](第四级) tr[16](第五级)这些数也存下1这个数字。接着我们读入2这个数字,此时tr[2]更新为1+2=3,tr[4](第三级) tr[8](第四级) tr[16](第五级)也跟着更新,当读入3这个数字的时候,毫无疑问,这个时候跟tr[1] tr[2] 已经没有关系了,只需要更新tr[3]=3 tr[4]=6 tr[8]=6 tr[16]=6。依次类推。
int lowbit(int x)
{
return x&-x;
}
修改某个元素
void add(int x,int v)
{
for(int i = x; i <= n; i += lowbit(i);
tr[x] += v;
}
子数列连续和
int query(int x)
{
int res = 0;
for(int i = x; i ; i -= lowbit(i))
res += tr[i];
return res;
}