树状数组是什么呢,就是长的像树的数组。
这个数组主要解决的问题一般是区间问题,比如区间和,区间最大值,最小值,或者区间值改变后的和这一类的题目。
一般做这类的题目我们用暴力(这个算法忽略)或者线段树,但是线段树开的数组比树状数组的大,线段树开的数组大小是 n>>2 ,树状数组的大小是 n ,在一些极端情况下还是用树状数组比较好。
先说个计算公式,叫lowbit,x= m & ( - m ),假如m=6,x=2,这个意思就是从后往前数二进制第几个数是1,(6)2=110,第二位数1,所以lowbit(6)=2
这个计算公式记住就行了,下面会讲作用
先给个图
图中,A数组就是我们输入的数组,C数组就是算法之后的数组。那么现在来解释C数组怎么来的。
C[1]=A[1]
C[2]=C[1]+A[2]
C[3]=A[3]
C[4]=A[4]+C[2]
........(后面就不写了)
我们发现,二进制尾数为1的C[k]=A[k],尾数为0的C[k]=C[lowbit(m1)+m1]+C[lowbit(m2)+m2]......+C[lowbit(mn)+mn]
比如C[8],lowbit(4)=4,4+4=8,lowbit(6)=2,6+2=8,lowbit(7)=1,7+1=8,而图中C[8]正好是C[4]+C[6]+C[7]
同样其他的C[k]也是这样算的,所以就我们要操作树状数组就要用到之前说的lowbit公式
然后说一下大概的操作吧,主要就是查询和修改
修改比较好了,比如我要修改A[3]的数据,那么C[3], C[4], C[8]都要更新数据,我们就从最底下的开始更新一直到最上面
void update(int x,int add) //x表示数组A[x],add表示更新的值
{
while (x<=n)
{
num[x]=num[x]+add; //num[x]就表示C[x]
x=x+lowbit(x);
}
return ;
}
通过之前的演示我们知道C[k]=C[lowbit(m1)+m1]+C[lowbit(m2)+m2]......+C[lowbit(mn)+mn](还一个不复制了
那么如果其中一个值改变了,C[k]也是要改变的,我们就可以用lowbit公式来找到上一层。所以代码中的while就是通过lowbit来找到上一层所在的位置,一直这样找直到最高层
还有就是查询了。查询[ i , j ]范围内的和,我们一般是先找[0, j] 的和在减去[0, i-1]的和
假如我们要找[4,6]的和,我们先找[0,6]的和,但是我们可以看出,C[6]=A[5]+A[6],之前的就没有加上。这时候我们还是要用到lowbit。lowbit(6)=2,很明显我们要找的是C[6-2]=C[6-lowbit(6)]的值,所以只要一直找完C[x-lowbit(x)]就行了(别问我为什么是这样,查了一下其它的博客,说是这样算得出的是上一个父节点。。。不懂
int query(int x)
{
int sum=0;
while (x>0)
{
sum=sum+num[x];
x=x-lowbit(x);
}
return sum;
}
树状数组大概就是这样子的。
最后说句,输入就是更新