树状数组

首先,先讲一讲什么是树状数组。

(下面一段,引用至百度百科)

树状数组(Binary Indexed Tree(B.I.T), Fenwick Tree)是一个查询和修改复杂度都为log(n)的数据结构。主要用于查询任意两位之间的所有元素之和,但是每次只能修改一个元素的值;经过简单修改可以在log(n)的复杂度下进行范围修改,但是这时只能查询其中一个元素的值(如果加入多个辅助数组则可以实现区间修改与区间查询)。

 

 

假设数组a[1..n],那么查询a[1]+...+a[n]的时间是log级别的,而且是一个在线的数据结构,支持随时修改某个元素的值,复杂度也为log级别。

来观察这个图:

令这棵树的结点编号为C1,C2...Cn。令每个结点的值为这棵树的值的总和,那么容易发现:
C1 = A1
C2 = A1 + A2
C3 = A3
C4 = A1 + A2 + A3 + A4
C5 = A5
C6 = A5 + A6
C7 = A7
C8 = A1 + A2 + A3 + A4 + A5 + A6 + A7 + A8
...
C16 = A1 + A2 + A3 + A4 + A5 + A6 + A7 + A8 + A9 + A10 + A11 + A12 + A13 + A14 + A15 + A16
这里有一个有趣的性质:
设节点编号为x,那么这个节点管辖的区间为2^k(其中k为x二进制末尾0的个数)个元素。因为这个区间最后一个元素必然为Ax,
所以很明显:Cn = A(n – 2^k + 1) + ... + An

 所以首先,定义一个名叫lowbit的函数。

#define lowbit(x)(x&(-x))
//#define lowbit(x)(x&(x^(x–1))

看了代码,咱们再来讲讲,lowbit用来干啥。

&,是位运算与,通常用于二进制取位操作,例如一个数 &1的结果就是取二进制的最末位。

我们假设x=10(10进制),它的二进制是001010。而-x(-10)的二进制就是把它的0变成1,1变成0,末尾再加1,等于110110。001010 & 110110 = 000010

所以,lowbit的作用是求出m的二进制表示的末尾1的位置。

 

接下来,我们定义一个add函数。add函数用来维护数组。

void add(int x,int y){
    while(x<=n){
        a[x]+=y;
        x+=lowbit(x);
    }
}

add函数是将数组的第x位加y,并维护数组。

 

能改变还不够,我们还要能求和。所以我们定义一个sum函数,用来求前x项的和。

int sum(int x){
    int ans=0;
    while(x>0){
        ans+=t[x];
        x-=lowbit(x);
    }
    return ans;
}

但是,这只是求前x项的和,要是要求x到y的和,该怎么办呢?

没事,只要求出前y项的和,再减去x,就行了。

 

下面是完整的代码:

 1 #include<bits/stdc++.h>
 2 #define lowbit(x) (x&(-x))
 3 using namespace std;
 4 
 5 int a[500001];
 6 int n,m;
 7 
 8 void add(int x,int y){
 9     while(x<=n){
10         a[x]+=y;
11         x+=lowbit(x);
12     }
13 }
14 int sum(int x){
15     int num=0;
16     while(x>0){
17         num+=a[x];
18         x-=lowbit(x);
19     }
20     return num; 
21 }
22 
23 int main(){
24     cin>>n>>m;
25     int x,y,z;
26     for(int i=1;i<=n;i++){cin>>x;add(i,x);}
27     for(int i=1;i<=m;i++){
28         cin>>x>>y>>z;
29         if(x==1)add(y,z);
30         else cout<<sum(z)-sum(y-1)<<endl;
31     }
32 }

这是我的第一篇博客,有不好之处,尽请谅解。

posted @ 2020-04-11 09:45  __zyh  阅读(28)  评论(0)    收藏  举报