树状数组
相比于线段树,节省了很多的内存
特殊用处:求逆序对以及在CDQ分治里嵌套用
一维树状数组
黑色部分代表原来的数组,用\(A[i]\)表示,红色部分代表树状数组,用\(C[i]\)表示。
\(C[1] = A[1]\\C[2] = A[1] + A[2]\\C[3]=A[3]\\C[4]=A[1]+A[2]+A[3]+A[4]\\C[5]=A[5]\\C[6] = A[5]+A[6]\\C[7] = A[7]\\C[8]=A[1]+A[2]+A[3]+A[4]+A[5]+A[6]+A[7]+A[8]\)
发现有规律,\(C[i] = A[i - 2^k + 1] + A[i - 2^k+2]+...A[i]\)k为二进制中最低位到最高位连续0的长度
那么对于求和,求前7项和,\(sum = C[7]+C[6]+C[4]\)
可以得到\(SUM[i] = C[i] + C[i - 2^{k_1}]+C[(i-2^{k_1}) -2^{k_2}]+...\)
lowbit
定义\(lowbit(x)\)是x的二进制表示形式中最低位的1和后面的0组成的数
二进制的负数是正数取反加1。如果x = 12(1100),那么-x = -12(0100)。lowbit(x)=4
int lowbit(int x){
return x & (-x);
}
更新
对于单点修改,如果要修改\(A[3]\)的值,那么对于\(C[3],C[4],C[8]\)都要进行修改
是一个向右上跳的过程
void update(int i, int k){
for(; i <= n; i += lowbit(i))c[i] += k;
}
查询
求\([1,i]\)的和。比如查询[1,5]的和,就是\(C[4]+C[5]\)
是一个向左上角跳的过程
int query(int i){
int ans = 0;
for(; i > 0; i -= lowbit(i))ans += c[i];
return ans;
}
因为求出来的是前缀和形式,所以对于求[L,R],可以用query(R) - query(L-1);
模板
单点修改 + 区间查询
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int N = 1e5 + 5;
struct BIT{
int c[N];
BIT(){memset(c, 0, sizeof(c));}
int lowbit(int x){
return x & (-x);
}
void update(int x, int k){// 修改a[x] 的值
for(; x <= N; x += lowbit(x))
c[x] += k;
}
int query(int x){//查询[1, x]的值
int ans = 0;
for(; x > 0; x -= lowbit(x))
ans += c[x];
return ans;
}
};
int main(){
BIT b;
return 0;
}
区间修改,单点查询
利用前缀和和差分思想,初始的树状数组是一个差分数组 对于每一个区间[l,r]修改,变为b[l]++, b[r + 1]-- 对于单点查询,就是[1,x]的前缀和
#include <iostream>
#include <cstdio>
#include <cstring>
#define ll long long
using namespace std;
const int N = 5e5 + 5;
int n, m;
struct BIT{
ll c[N];
BIT(){memset(c, 0, sizeof(c));}
ll lowbit(ll x){
return x & (-x);
}
void Add(int x, ll k){
for(; x <= N; x += lowbit(x))c[x] += k;
}
ll Ask(ll x){//[1, x]和
ll ans = 0;
for(;x; x -= lowbit(x))ans += c[x];
return ans;
}
};
int main(){
BIT bit;
ll last = 0, now;
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; i++){
scanf("%lld", &now);
bit.Add(i, now - last);//差分
last = now;
}
for(int i = 1; i <= m; i++){
ll op, l, r, c;
scanf("%lld", &op);
if(op == 1){
scanf("%lld%lld%lld", &l, &r, &c);
bit.Add(l, c); bit.Add(r + 1, -c);//差分
}else {
scanf("%lld", &l);
printf("%lld\n", bit.Ask(l));
}
}
return 0;
}
二维树状数组
struct BIT{
int c[N][N];
BIT(){memset(c, 0, sizeof(c));}
int lowbit(int x){
return x & (-x);
}
void add(int x, int y, int k){//点a[x][y] + k
while(x <= N){
while(y <= N){
c[x][y] += k;
y += lowbit(y);
x += lowbit(x);
}
}
}
int sum(int x, int y){//求[1][1] 到[x][y] 的和
int ans = 0;
while(x){
while(y){
ans += c[x][y];
x -= lowbit(x);
y -= lowbit(y);
}
}
return ans;
}
};