树状数组
树状数组可以实现
- 将某一个数加上
- 求出某区间每一个数的和
树状数组只能维护运算的前缀和,当逆元存在时才能得到区间的信息。比如取
单点增加,区间求和
建树只需要
cpp
void build() {
for (int i = 1; i <=n; i++) {
cc[i] += aa[i];
int j = i + (i&(-i));
if(j <= n)
cc[j] += cc[i];
}
}
void build() {
for (int i = 1; i <=n; i++) {
cc[i] += aa[i];
int j = i + (i&(-i));
if(j <= n)
cc[j] += cc[i];
}
}
查询前缀和
cpp
int ask(int *cc, int x) {
int sum = 0;
while (x >= 1) {
sum += cc[x];
x -= x&(-x);
}
return sum;
}
int ask(int *cc, int x) {
int sum = 0;
while (x >= 1) {
sum += cc[x];
x -= x&(-x);
}
return sum;
}
单点增加,在
cpp
void add(int *cc, int x, int k) {
while (x <= n) {
cc[x] += k;
x += x&(-x);
}
}
void add(int *cc, int x, int k) {
while (x <= n) {
cc[x] += k;
x += x&(-x);
}
}
区间加 & 单点查询
维护数组
cpp
int bb[N];
void badd(int l, int r, int k) {
add(bb, l, k);
add(bb, r+1, -k);
}
int bask(int x) {
return ask(bb, x) + aa[x];
}
int bb[N];
void badd(int l, int r, int k) {
add(bb, l, k);
add(bb, r+1, -k);
}
int bask(int x) {
return ask(bb, x) + aa[x];
}
区间加 & 区间求和
维护数组
因此还需要两个树状数组来维护 cask
。
cpp
int bb1[N], bb2[N];
void cadd(int l, int r, int k) {
add(bb1, l, k);
add(bb1, r+1, -k);
add(bb2, l, l * k);
add(bb2, r+1, -(r+1) * k);
}
int cask(int x) {
return (x+1) * ask(bb1, x) + ask(cc,x) - ask(bb2,x);
}
int bb1[N], bb2[N];
void cadd(int l, int r, int k) {
add(bb1, l, k);
add(bb1, r+1, -k);
add(bb2, l, l * k);
add(bb2, r+1, -(r+1) * k);
}
int cask(int x) {
return (x+1) * ask(bb1, x) + ask(cc,x) - ask(bb2,x);
}