文章目录
引入问题:
我们要输入一个序列a,要求在线修改一个值或者求一个子串的所有元素的和。
1.朴素算法(说白了就是暴力)
修改值:直接改,时间复杂度:O(1)
求字串的所有元素的和:一个循环,时间复杂度:O(n)
有那么有点慢。
2.前缀和
求字串的所有元素的和:一个计算:sun[i]-sum[i-1],时间复杂度:O(1)
修改值:有点麻烦了,要从 i 循环到 n,时间复杂度:O(n)
也有那么有点慢。
综上所述,好像差不了太多呀!
于是,我们的树状数组闪亮登场了!
但在了解树状数组之前,我们先了解一下lowbit();
lowbit()函数
lowbit()函数所求的就是最低位1的位置,所以可以通过位运算来计算 。
计算方法1:
int lowbit(int x) {
return x&(x^(x-1));
}
首先设x=6,即其二进制为110 。于是我们使 x-1=101 可以发现,当我们将一个二进制数减一时,从最低位一(即lowbit)开始向后的部分与之前全部相反,因为减去的1对后面的每一位都有影响,同时因为是二进制,影响就是让每一位都取反了。
110就变成101
从最低位一(第二位)开始向后全部相反了 所以我们再与 x 异或一下,那么从lowbit开始往后全是1
110^101=011
然后我们再用x与新数按位与一下 因为 x lowbit 以前的部分是1或0,lowbit 是1,之后的部分都是0,新数 lowbit 之前的部分都是0,lowbit 是1,之后的部分都是1 所以与完之后他们的交集就是 lowbit。
110&011=010
如此,我们就求出了lowbit。
计算方法2:
int lowbit(int x) {
return x & -x;
}
原理与上面的方法不尽相同 这个式子运用了计算机的补码计算原理 补码计算简单来讲就是原码的反码反加一 如:
0110(2)=6
变为反码后为 0001
再加一为 0010 即它的补码
可以发现变为反码后 x 与反码数字位每一位都不同, 所以当反码加1后神奇的事情发生了,反码会逢1一直进位直到遇到0,且这个0变成了1,所以这个数最后面出现了一个 100… 串。 由于是反码,进位之后由于1的作用使进位的部分全部取反及与原码相同,所以可以发现 lowbit 以前的部分 x 与其补码即 -x 相反, lowbit x 与 -x 都是1,lowbit 以后 x 与 -x 都是0 所以 x&-x 后除了 lowbit 位是1,其余位都是0。
但lowbit与树状数组有神马关系呢?下面将会为你讲解。
树状数组
树状数组可以解决大部分基于区间上的更新以及求和问题。
树状数组可以解决的问题都可以用线段树解决,但这两者有区别:树状数组的系数要少很多,我们可以这样想:高精算法可以解决高精问题,也可以解决A+B这种简单的问题,但我敢保证没人会用高精算法来做A+B问题(除非他想装逼)。
树状数组像前缀和,但也有不同之处。请观下图:
有一些C[i]只有一部分的数组的和。
而且很有规律。
C[1]=A[1]
C[2]=A[1]+A[2]
C[3]=A[3]
C[4]=A[1]+A[2]+A[3]+A[4]
C[5]=A[5]
…
其实,我们可以发现,C[i]是x个相邻元素的和,其中x=lowbit(x)。
豁然开朗了。
树状数组的操作:
1.修改值(update):
我们要把第k个元素增加x
我们就每一次相加了后,寻找这个元素的父亲,也就是加lowbit(k)
void update(int k,int x) {
for(;k<=n;k+=lowbit(k))
BIT[k]+=x;
return ;
}
2.求L~R的值(update):
这里的ask函数是求1~x的值。
那么L~R的值就是ask( r )-ask( l-1 )
这个ask函数感觉就和update函数互逆。
int ask(int x) {
int ans=0;
for(int i=x;i;i-=lowbit(i))
ans+=BIT[i];
return ans;
}
例题:
1.单点修改,区间查询:
你有一个长度为n的数组,你要进行q次操作。
操作有两类:
1. 1 i x:给定i,x,将a[i]加上x。
2. 2 l r:求l~r的和。
样例输入: 样例输出:
3 2 6
1 2 3
1 2 0
2 1 3
n,q小于等于1000000。
这道题就是我们的引入问题:
#include <bits/stdc++.h>
using namespace std;
int n,q,a[1000005];
long long BIT[1000005];
long long lowbit(int x) {
return x & -x;
}
void update(int k,int x) {
for(int i=k;i<=n;i+=lowbit(i))
BIT[i]+=x;
return ;
}
long long ask(int x) {
long long ans=0;
for(int i=x;i;i-=lowbit(i))
ans+=BIT[i];
return ans;
}
int main() {
scanf("%d %d",&n,&q);
for(int i=1;i<=n;i++) {
scanf("%d",&a[i]);
update(i,a[i]);
}
for(int i=1;i<=q;i++) {
int t,l,r;
scanf("%d %d %d",&t,&l,&r);
if(t==