特点,优点
- 树状数组是一种支持 单点修改 和 区间查询 的,代码量小的数据结构
- 普通树状数组维护的信息及运算要满足 结合律 且 可差分,如加法、乘法、异或等。
特点:
- 区间长度 = lowbit
- 一个x区间的正上层编号为x+lowbit(x) ,如c[7] 的正上层的序列为c[8],而c[7+lowbit(7)] = c[8]
c[4] = c[2 +lowbit(2)],因此在修改值的时候,只需要不断加上lowbit(i)就可以找到上方的所有序列进行修改
- 管辖区间 为 [x-lowbit(x)+1,x]
- lowbit(x) = x&-x;
核心代码
求和
单点修改
树状数组高效的原因
- 分块思想:树状数组的核心在于将数组分成多个块,每个块维护一部分区间的信息。这种分块思想使得每次更新和查询操作只需要处理log(n)个块,从而降低了时间复杂度。
- 位运算优化:树状数组在确定更新和查询路径时,通常使用位运算(Lowest One Bit,LOB)来快速定位下一个需要处理的块。具体来说,通过计算索引的最低有效位(LOB),可以快速找到父节点或子节点,这个操作的时间复杂度是O(1)。
- 稀疏表示:树状数组实际上是一个稀疏树结构,它只存储了部分必要的信息。这种稀疏表示减少了存储空间,同时也减少了操作时的计算量。
- 预处理和延迟更新:树状数组在初始化时只需要O(n)的时间复杂度来构建整个结构,之后的每次更新和查询都可以在O(logn)的时间内完成。此外,树状数组支持延迟更新,即可以在不立即计算结果的情况下记录更新操作,等到需要结果时再计算,这可以进一步优化性能。
- 避免重复计算:在树状数组中,每个节点只计算和存储了它所负责的区间信息,这些信息在更新和查询时会被多次利用,避免了重复计算,从而提高了效率。
- 递推关系:树状数组利用了递推关系(父子节点之间的关系)来快速计算区间和。每个节点存储的是其所有直接子节点的信息之和,这使得在查询时可以通过累加这些子节点的信息来得到所需的区间和。
例题
树状数组不仅仅可以用来维护前缀和,也可以维护差分,减少时间复杂度
洛谷 树状数组模板2
#include<iostream>
#define int long long
using namespace std;
const int N = 5e5 + 50;
int c[N];
int a[N];
int n, m;
int lowbit(int x){
return x & -x;
}
void update(int x,int k){
while(x<=n){
c[x] += k;
x+= lowbit(x);
}
}
int getsum(int x){
int ans = 0;
while(x>0){
ans += c[x];
x -= lowbit(x);
}
return ans;
}
signed main(){
cin >> n >> m;
for (int i = 1; i <= n; i++){
cin >> a[i];
}
while(m--){
int op;
cin >> op;
if(op==1){
int x, y, k;
cin >> x >> y >> k;
update(x, k);//重点
update(y + 1, -k);//重点
}
else{
int o;
cin >> o;
cout << a[o]+getsum(o)<<endl;//原来的数再加上总变化
}
}
return 0;
}