强推学习树状数组视频:完全理解并深入应用树状数组 | 支持多种动态维护区间操作
P3374 【模板】树状数组 1
P3368 【模板】树状数组 2
什么是树状数组
树状数组是用数组模拟树形结构
树状数组可以解决的问题
- 单点修改
- 区间修改
- 区间查询
- 区间修改
树状数组的优缺点
两者修改查询操作时间复杂度
操作 | 朴素数组 | 树状数组 |
---|---|---|
单点修改 | 1 | log2(n) |
区间查询 | n | log2(n) |
多次操作 | n^2 | nlog2(n) |
- 构建树状数组
for(int i = 1; i <= n; i++){
scanf("%d", &a);
add(i, a);
}
- 单点修改
void add(int x, int k){
for( ; x <= n; x += x&-x){
t[x] += k;
}
}
e.g.
对第 i 个元素加K => add(i, k)
- 区间查询
void add(int x, int k){
for( ; x <= n; x += x&-x){
t[x] += k;
}
}
e.g.
查询[l, r]的和 => ask(r) - ask(l - 1)
对树状数组进行区间修改和单点查询是需要引入差分数组
-构建树状数组有些不同
for(int i = 1; i <= n; i++){
scanf("%d", &a);
add(i, a - ner);
ner = a;
}
- 单点查询
e.g.
查询l的值 => ask(l)
- 区间修改
e.g.
[l, r] + k => add(l, k); add(r + 1, -k)
P3374 【模板】树状数组 1
#include <cstdio>
using namespace std;
const int maxn = 5e5 + 5;
int n, m;
long long t[maxn];
void add(int x, int k){
for( ; x <= n; x += x&-x){
t[x] += k;
}
}
long long ask(int x){
long long ans = 0;
for( ; x; x -= x&-x){
ans += t[x];
}
return ans;
}
int main(){
scanf("%d%d", &n, &m);
int a;
for(int i = 1; i <= n; i++){
scanf("%d", &a);
add(i, a);
}
int method, x, y, k;
for(int i = 1; i <= m; i++){
scanf("%d%d%d", &method, &x, &y);
if(method == 1){
add(x, y);
}
else{
printf("%ld\n", (ask(y) - ask(x - 1)));
}
}
return 0;
}
P3368 【模板】树状数组 2
#include <cstdio>
using namespace std;
const int maxn = 5e5 + 5;
int n, m;
long long t[maxn];
void add(int x, int k){
for( ; x <= n; x += x&-x){
t[x] += k;
}
}
long long ask(int x){
long long ans = 0;
for( ; x; x -= x&-x){
ans += t[x];
}
return ans;
}
int main(){
scanf("%d%d", &n, &m);
int a, ner = 0;
for(int i = 1; i <= n; i++){
scanf("%d", &a);
add(i, a - ner);
ner = a;
}
int method, x, y, k;
while(m--){
scanf("%d", &method);
if(method == 1){
scanf("%d%d%d", &x, &y, &k);
add(x, k);
add(y + 1, -k);
}
else{
scanf("%d", &x);
printf("%d\n", ask(x));
}
}
return 0;
}