树状数组的作用:①单点修改 ②快速区间查询
与普通前缀和对比:
普通前缀和查询是O(1)的,但是如果涉及修改,便是O(n)的复杂度
lowbit(x)的含义:x的二进制表示末尾有k个零,则lowbit(x)=2^k
int lowbit(int x){
return x & -x;
}
树状数组C[x] 表示 ( x-lowbit(x) , x ] 这段的和
1.单点修改,即将原数组A[x]+v, C[x] 如何变化?
void add(int x,int v){
for(int i=x;i<=n;i+=lowbit(i))
c[i]+=v;
}
2.区间查询,即求某前缀和(A[1]…A[x])
int query(int x){
int res=0;
for(int i=x;i>0;i-=lowbit(x)) res+=c[i];
return res;
}
树状数组例题: 数星星
首先将问题抽象为模型:
在对每一个星星(x,y)进行操作时,先统计其左下(包括正左和正下)所有的星星之和,
并将这个等级的星星数+1,再把这颗星星插入树状数组中。
求和操作:将二维的个数统计转化为一维的统计方式
代码如下:
#include<iostream>
using namespace std;
const int N=32010;
int c[N],ans[N];
int n;
int lowbit(int x){
return x & -x;
}
int query(int x){
int res=0;
for(int i=x;i;i-=lowbit(i)) res+=c[i];
return res;
}
void add(int x,int v){
for(int i=x;i<N;i+=lowbit(i)) c[i]+=v;
}
int main(){
cin>>n;
for(int i=1;i<=n;i++){
int x,y;cin>>x>>y; x++;
ans[query(x)]++;
add(x,1);
}
for(int i=0;i<n;i++) cout<<ans[i]<<endl;
return 0;
}