树状数组基础
树状数组是一个查询和修改复杂度都为log(n)的数据结构。主要用于数组的单点修改&&区间求和.
另外一个拥有类似功能的是线段树.
具体区别和联系如下:
1.两者在复杂度上同级, 但是树状数组的常数明显优于线段树, 其编程复杂度也远小于线段树.
2.树状数组的作用被线段树完全涵盖, 凡是可以使用树状数组解决的问题, 使用线段树一定可以解决, 但是线段树能够解决的问题树状数组未必能够解决.
3.树状数组的突出特点是其编程的极端简洁性, 使用lowbit技术可以在很短的几步操作中完成树状数组的核心操作,其代码效率远高于线段树。
(以上部分摘自:bestsort)
上图是树状数组的结构,可以发现:
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];
令lowbit=2^k
int lowbit(int x){
return x&(-x);
}
那么 C[i]=A[i-lowbit(i)+1]+A[i-lowbit(i)+2]+......A[i];
区间查询:
例如求区间[1,7]的值之和:
sum[7]=A[1]+A[2]+A[3]+A[4]+A[5]+A[6]+A[7] ; 前i项和
C[4]=A[1]+A[2]+A[3]+A[4]; C[6]=A[5]+A[6]; C[7]=A[7];
可以推出: sum[7]=C[4]+C[6]+C[7];
序号写为二进制: sum[(111)]=C[(100)]+C[(110)]+C[(111)];
int sum(int x){
int res=0;
for(int i=x;i>0;i-=lowbit(i)){
res+=c[i];
}
return res;
}
单点更新:
例如把点A[1]的值加m,要更改C[1],C[2],C[4],C[8]
(1) C[1]+=m
(1)+lowbit(1)=2 C[2]+=m
(10)+lowbit(2)=4 C[4]+=m
(100)+lowbit(4)=8 C[8]+=m
void add(int x){
for(int i=x;i<=N;i+=lowbit(i)){
c[i]++;
}
}
要注意的是:
1.树状数组的存储空间为O(N),不像线段树要开N<<2的空间,因为它存的只有C[1],C[2],C[3],C[4]……C[N]。
2.树状数组从1开始存,因为0&(-0)=0无法进行更新。
一道模板题:
题意:有n颗星星,按“y轴从小到大,若y轴相等,按x轴从小到大”的规律给出,求出每个星星的的层数,层数的定义是“y坐标不超过该星星,且x坐标不超过该星星的星星个数”,输出在这个层数星星的个数
思路:由于星星给出的规则,当给出了i个星星时,y坐标大于yi的以及,y==yi&&x>xi的都没给出,而且也不参与星星的层数计算,给出的星星:y<=yi,所以只要找出x<=xi的星星个数就可以啦~
代码如下:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<string>
#include<cstring>
#include<queue>
#include<stack>
#include<cmath>
#include<set>
#include<map>
using namespace std;
#define ll long long
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
typedef pair<int,int>P;
const int INF=0x3f3f3f3f;
const int N=32010;
int c[N],num[N];
int lowbit(int i){
return i&(-i);
}
int sum(int x){
int res=0;
for(int i=x;i>0;i-=lowbit(i)){
res+=c[i];
}
return res;
}
void add(int x){//x点加一
for(int i=x;i<=N;i+=lowbit(i)){
c[i]++;
}
}
int main(){
int n,x,y;
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d%d",&x,&y);
x++;
int level=sum(x);
num[level]++;
add(x);
}
for(int i=0;i<n;i++){
printf("%d\n",num[i]);
}
}