树状数组
前言:树状数组可以解决的,线段树也可以解决,树状数组只是在一些问题上有奇效,优先建议练习时还是优先使用线段树
**本质上,解决的问题实际是 差分数组 − > 原始数组 − > 前缀和数组 差分数组->原始数组->前缀和数组 差分数组−>原始数组−>前缀和数组这其中要使用其二或其三的一种使用树优化效率的一种方法
1. 单点修改+区间查询
const int N = 1000;
#define lowbit(x) ((x) & - (x))
int tree[N]={0};
void update(int x, int d) { //单点修改:修改元素a[x], a[x] = a[x] + d
while(x <= N) {
tree[x] += d;
x += lowbit(x);
}
}
int sum(int x) { //查询前缀和:返回前缀和sum = a[1] + a[2] +... + a[x]
int ans = 0;
while(x > 0){
ans += tree[x];
x -= lowbit(x);
}
return ans;
}
2. 区间修改+单点查询
在上面的基础上,每个节点由差分数组组成
对于区间,左端L节点 + d +d +d,右端R+1节点 − d -d −d
用sum求得单点值
3. 区间修改+区间查询
在上面的基础上,每个节点由差分数组组成
s u m ( L , R ) = s u m ( 1 , R ) − s u m ( 1 , L − 1 ) sum(L,R)=sum(1,R)-sum(1,L-1) sum(L,R)=sum(1,R)−sum(1,L−1)
s u m ( 1 , R ) = ∑ i = 1 k D i − ∑ i = 1 k ( i − 1 ) D i sum(1,R)=\sum_{i=1}^{k}{D_i}-\sum_{i=1}^k{(i-1)D_i} sum(1,R)=i=1∑kDi−i=1∑k(i−1)Di
从而实现从差分数组到前缀和的转变
4. 单点修改+区间最值
const int N = 2e5+10;
int n,m,a[N],tree[N];
int lowbit(int x){return x&(-x);}
void update(int x,int value){ //更新tree[x]的最大值
while(x<=n){
tree[x]=value;
for(int i=1;i<lowbit(x);i<<=1)
tree[x]=max(tree[x],tree[x-i]);
x+=lowbit(x);
}
}
int query(int L,int R){ //关于区间[L,R]的最值
int ans=;
while(L<R){
ans=max(ans,a[R]);
R--;
while(R-L>=lowbit(R)){
ans=max(ans,tree[R]);
R-=lowbit(R);
}
}
return ans;
}