C++数据结构·树状数组
树状数组定义
树状数组 ( F e n w i c k (Fenwick (Fenwick T r e e 或 B i n a r y Tree或Binary Tree或Binary I n d e x e d Indexed Indexed T r e e ) Tree) Tree)
是一种高效的数据结构,用于动态维护前缀和或区间信息
其核心特点是通过二进制拆分优化单点更新和前缀查询操作
时间复杂度: O ( log n ) O(\log n) O(logn)
核心(函数)部分
- lowbit
用于获取一个数二进制表示中最低位的1及其对应数值
即按位与(&)
原理: 就是用x&-x补码表示,负数以补码形式存储
补码 = 负数 ( 二进制 ) 取反 + 1 补码=负数(二进制)取反+1 补码=负数(二进制)取反+1
当x与-x进行按位与操作时,会保留x的最低位的1,其余位均为0
int lowbit(int x){
return x&-x;//按位与
}
- sum
用于计算从数组起始位置(通常为1)到指定位置的前缀和
即区间查询
原理: 树状数组通过lowbit函数将原数组的索引转换为二进制形式,利用最低有效位 ( L S B ) (LSB) (LSB)的性质实现快速前缀和查询,每个节点存储的并非单点值,而是特定区间的部分和,通过叠加这些区间和得到最终结果
int sum(int x,int y[]){
int ans=0;
for(int i=x;i;i-=lowbit(i))
ans+=y[i];
return ans;
}//区间查询
- update(add)
用于单点更新(修改)
即单点修改
原理: 树状数组的核心思想是通过二进制低位技术Lowbit来高效更新和查询,每个节点存储的是一个区间的和,而非单个元素的值,add操作通过不断调整索引来更新所有相关的区间和
void add(int x,int y,int z[]){
for(int i=x;i<=n;i+=lowbit(i))
z[i]+=y;
}//单点修改
这里注意先后顺序,先写 l o w b i t lowbit lowbit再写 s u m , a d d sum,add sum,add
例题展示
传送门 : 传送门: 传送门: 点这里 点这里 点这里
P3374 【模板】树状数组 1
题目描述
如题,已知一个数列,你需要进行下面两种操作:
-
将某一个数加上 x x x;
-
求出某区间每一个数的和。
输入格式
第一行包含两个正整数 n , m n,m n,m,分别表示该数列数字的个数和操作的总个数。
第二行包含 n n n 个用空格分隔的整数,其中第 i i i 个数字表示数列第 i i i 项的初始值。
接下来 m m m 行每行包含 3 3 3 个整数,表示一个操作,具体如下:
-
1 x k含义:将第 x x x 个数加上 k k k; -
2 x y含义:输出区间 [ x , y ] [x,y] [x,y] 内每个数的和。
输出格式
输出包含若干行整数,即为所有操作 2 2 2 的结果。
输入输出样例 #1
输入 #1
5 5
1 5 4 2 3
1 1 3
2 2 5
1 3 -1
1 4 2
2 1 4
输出 #1
14
16
说明/提示
【数据范围】
对于 30 % 30\% 30% 的数据, 1 ≤ n ≤ 8 1 \le n \le 8 1≤n≤8, 1 ≤ m ≤ 10 1\le m \le 10 1≤m≤10;
对于 70 % 70\% 70% 的数据, 1 ≤ n , m ≤ 1 0 4 1\le n,m \le 10^4 1≤n,m≤104;
对于 100 % 100\% 100% 的数据, 1 ≤ n , m ≤ 5 × 1 0 5 1\le n,m \le 5\times 10^5 1≤n,m≤5×105。
数据保证对于任意时刻, a a a 的任意子区间(包括长度为 1 1 1 和 n n n 的子区间)和均在 [ − 2 31 , 2 31 ) [-2^{31}, 2^{31}) [−231,231) 范围内。
样例说明:

故输出结果 14 14 14 和 16 16 16。
分析
看题目
输入内容较多,逐行分析:
【line1】输入数列长度及操作数量
【line2】输入数列初值
【line3~line3+m】输入选择(choose)
如果是1,那么输入 x , k x,k x,k,使数列 t r e e [ x ] + = k ; tree[x]+=k; tree[x]+=k;
如果是2,那么输入 x , y x,y x,y,输出数列 t r e e [ x ] 到 t r e e [ y ] 的和 tree[x]到tree[y]的和 tree[x]到tree[y]的和
思路解析
先看重点line3~line3+m
定义变量chose作为选择参数
判断
若为1则进行 a d d add add操作
若为2则进行 s u m sum sum操作
根据上文的函数模板,很轻松就可以 A C ( A c c e p t e d = 通过 ) AC(Accepted=通过) AC(Accepted=通过)
代码
#include<bits/stdc++.h>
using namespace std;
int n,m,tree[5000005];
int lowbit(int x){
return x&(-x);//按位与
}
int sum(int x,int y[]){
int ans=0;
for(int i=x;i;i-=lowbit(i))
ans+=y[i];
return ans;
}//区间查询
void add(int x,int y,int z[]){
for(int i=x;i<=n;i+=lowbit(i))
z[i]+=y;
}//单点修改
int main(){
cin>>n>>m;//执行【line1】操作
for(int i=1;i<=n;i++){
int a;
cin>>a;
add(i,a,tree);
}//执行【line2】操作,用add一个一个赋初值
for(int i=1;i<=m;i++){
int chose;
cin>>chose;//选择
if(chose==1){//操作1
int x,k;
cin>>x>>k;
add(x,k,tree);//修改/增加
}else{//操作2
int x,y;
cin>>x>>y;
cout<<sum(y,tree)-sum(x-1,tree)<<endl;
//输出x~y的区间和(即1~y的和减去1~x-1的和)
}
}
return 0;
}
题单提供
树状数组 树状数组 树状数组其中也有二叉堆,有空讲
~ 完结撒花 完结撒花 完结撒花 ~
附:例题和题单均来自 洛谷 洛谷 洛谷
内容实战可参考上方题单
下一篇预告:你猜
更多推荐
所有评论(0)