树状数组可以很方便地实现数组的动态修改求和,并且代码量非常小!经过拓展之后,还可以实现区间修改单点查询、求区间最值和第 K 大值(继续学习之后再更新)
基本功能的实现主要有三个函数,lowbit 、 add 、 getsum;
单点加值区间求和:
1 #include<stdio.h>
2 #include<string.h>
3
4 const int maxn=1e5+5; //数组大小
5
6 int c[maxn];
7
8 void init(){
9 memset(c,0,sizeof(c));
10 }
11
12 inline int lowbit(int x){return x&(-x);}
13 //lowbit函数,决定下一个/上一个覆盖区间
14
15 void add(int x,int a,int n){
16 for(int i=x;i<=n;i+=lowbit(i))c[i]+=a;
17 }
18 //单点加数
19
20 int sum(int x){
21 int ans=0;
22 for(int i=x;i>0;i-=lowbit(i))ans+=c[i];
23 return ans;
24 }
25 //区间求和
动态单点更新并区间求最值功能:
1 #include<stdio.h>
2 #include<string.h>
3
4 const int maxn=1e5+5; //数组大小
5 inline int lowbit(int x){return x&(-x);}
6 inline int max(int a,int b){return a>b?a:b;}
7 int a[maxn],ma[maxn]; //a为原数组,ma为最大值数组
8
9 void init(int n){
10 for(int i=1;i<=n;i++){
11 ma[i]=a[i];
12 for(int j=1;j<lowbit(i);j<<=1)
13 ma[i]=max(ma[i],ma[i-j]);
14 }
15 }
16 //init函数是构建最大值数组的函数,其中 n 是数组长度,a 是原数组,ma 是区间最大值数组,表示 [ i - lowbit [ i ] ,i ]区间内的最大值,对于每一个 i ,用被其覆盖的各个区间的最大值更新它。
17
18 void change(int ind,int v,int n){
19 a[ind]=v;
20 for(int i=ind;i<=n;i+=lowbit(i)){
21 ma[i]=v;
22 for(int j=1;j<lowbit(i);j<<=1)
23 ma[i]=max(ma[i],ma[i-j]);
24 }
25 }
26 //将第ind个数更新为v
27
28 int query(int l,int r){
29 int ans=a[r];
30 while(1){
31 ans=max(ans,a[r]);
32 if(l==r)break;
33 for(r-=1;r-l>=lowbit(r);r-=lowbit(r))
34 ans=max(ans,ma[r]);
35 }
36 return ans;
37 }
38 //查询 [ l , r ] 区间内的最大值,通过不断考虑 r 覆盖的区间中的最大值,将 r 不断左移直到等于 l ,返回这中间的最大值,对于每一次遍历的 r ,若 r 覆盖的区间在查询区间内,就直接使用该区间的最大值,然后左移 r 值原先 r 覆盖的区间左,若 r 覆盖的区间比查询区间大,就直接考虑 r 的值,然后将 r 左移至 k - 1 ;这样循环直到 l == r 。
树状数组还有区间加值单点查询的功能:
1 #include<stdio.h>
2 #include<string.h>
3
4 const int maxn=1e5+5; //数组大小
5
6 int c[maxn];
7
8 inline int lowbit(int x){return x&(-x);}
9
10 void change(int x,int v,int n){
11 while(x<=n){
12 c[x]+=v;
13 x+=lowbit(x);
14 }
15 }
16
17 int sum(int x){ //求x单点值
18 int s=0;
19 while(x){
20 s+=c[x];
21 x-=lowbit(x);
22 }
23 return s;
24 }
25
26 void update(int l,int r,int v){ //将区间[l,r]加v
27 change(l,v);
28 change(r+1,-v);
29 }