对于学习的线段树方面的,发现很多问题可以用它来求解,但是做题的时候发现,用线段树太容易tle也,很多次也是卡时间过的,然后就发现了树状数组。
首先我们搞明白树状数组是用来干嘛的,树状数组是一个查询和修改复杂度都为log(n)的数据结构。主要用于查询任意两位之间的所有元素之和,但是每次只能修改一个元素的值,经过简单修改可以在log(n)的复杂度下进行范围修改,但是这时只能查询其中一个元素的值(如果加入多个辅助数组则可以实现区间修改与区间查询)。
所以说,树状数组的求解和线段树的求解有些重合,但是功能没有线段树的那么广泛。但是对树状数组来说,其思想大概是用二进制的方法来实现。
先简单介绍下基本二进制的使用方法:
& 按位与 如果两个相应的二进制位都为1,则该位的结果值为1,否则为0
| 按位或 两个相应的二进制位中只要有一个为1,该位的结果值为1
^ 按位异或 若参加运算的两个二进制位值相同则为0,否则为1
~ 取反 ~是一元运算符,用来对一个二进制数按位取反,即将0变1,将1变0
<< 左移 用来将一个数的各二进制位全部左移N位,右补0 (<<1则是乘2)
>> 右移 将一个数的各二进制位右移N位,移到右端的低位被舍弃,对于无符号数,高位补0(>>1则是除2)
原理:lowbit
lowbit
这个函数的功能就是求某一个数的二进制表示中最低的一位1
,举个例子,x = 6
,它的二进制为110
,那么lowbit(x)
就返回2
,因为最后一位1
表示2
。
那么怎么求lowbit呢?把这个数的二进制写出来,然后从右向左找到第一个1(这个1就是我们要求的结果,但是现在表示不出来,后来的操作就是让这个1能表示出来),这个1不要动和这个1右边的二进制不变,左边的二进制依次取反,这样就求出的一个数的补码,说这个方法主要是让我们理解一个负数的补码在二进制上的特征,然后我们把这个负数对应的正数与该负数与运算一下,由于这个1的左边的二进制与正数的原码对应的部分是相反的,所以相与一定都为0,;由于这个1和这个1右边的二进制都是不变的,因此,相与后还是原来的样子,故,这样搞出来的结果就是lowbit(x)的结果。
然后后面的看到大佬的博客就直接贴个博客链接了,感觉大佬讲解的很详细了。
贴个大佬的模板:
int lowbit(int x){ return x & -x; } void update(int i,int val){ //单点更新 while(i<=n){ c[i]+=val; i+=lowbit(i); //由叶子节点向上更新树状数组c,从左往右更新 } } int sum(int i){ // 求区间[1,i]内所有元素的和 int ret=0; while(i>0){ ret+=c[i]; //从右往左累加求和 i-=lowbit(i); } return ret; }
原文博客链接:https://www.cnblogs.com/acgoto/p/8583952.html
原文地址:https://www.cnblogs.com/wushengyang/p/10319111.html