树状数组用一个数组C[i]来记录a[i-2^k +1] + ...+a[i]的和,其中k为i在二进制下末尾0的个数。
树状数组能够快速求出数组中连续一段元素的和以及快速修改数组中的元素;
具体实现:
求i在二进制下末尾0的个数;
int lowbit(int x){
return x&(-x);
}
或者
int lowbit(int x){
return x&(x^(x-1));
}
求和:能够快速求a[1] 到a[i]的和,时间复杂度为log(n);
int sum(int i){
int s = 0;
for (int k = i ; k>=0; k -= lowbit(k))
s += c[k];
return s;
}
更新:能在log(n)的时间内更新元素的值(将a[i]的值增加d);
void add(int i,int d){
for (int k = i; k <= n; k+=lowbit(k)){
c[k] += d;
}
}
Uva 12086 树状数组模板题;
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<climits>
#include<cctype>
#include<iostream>
#include<algorithm>
#include<queue>
#include<vector>
#include<map>
#include<stack>
#include<string>
#include<set>
#define ll long long
#define MAX 1000000
#define INF INT_MAX
#define eps 1e-6
using namespace std;
int c[MAX],a[MAX],n;
int lowbit(int x){
return x&(-x);
}
int sum(int i){
int s = 0;
for (int k = i ; k>0; k -= lowbit(k))
s += c[k];
return s;
}
void add(int i,int d){
for (int k = i; k <= n; k+=lowbit(k)){
c[k] += d;
}
}
int main(){
char s[10];
int cas = 0;
while (scanf("%d",&n) && n != 0){
memset(c,0,sizeof(c));
for (int i=1; i<=n; i++){ //树状数组建树方法,通过更新建树,时间复杂度为n*log(n),也有O(n)的方法;
scanf("%d",&a[i]);
add(i,a[i]);
}
if (cas) printf("\n");
cas++;
printf("Case %d:\n",cas);
int u,v;
while (scanf("%s",s)){
if (strcmp(s,"END") == 0) break;
scanf("%d%d",&u,&v);
if (s[0] == 'M'){
printf("%d\n",sum(v) - sum(u-1));
}
else {
add(u,v-a[u]); //如何更新元素
a[u] = v;
}
}
}
return 0;
}
注意:树状数组的下标是从1开始的,而不是从0开始。假如从0开始,则应该满足0 - lowbit(0) < 0,不成立;
二维树状数组:具体实现和一维类似,更新和查询的时间复杂度都是log(n) *log(m);
求和:
int sum(int i,int j){
int s = 0;
for (int u = i ; u > 0 ; u -= lowbit(u))
for (int v = j ; v >=0 ; v -= lowbit(v))
s += c[u][v];
return s;
}
更新:
void add(int i, int j, int d){
for (int u = i; u <= n; u += lowbit(u))
for (int v = j ; v <= m; v += lowbit(v))
c[u][v] += d;
}
poj1195 二维树状数组模板题;
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<climits>
#include<cctype>
#include<iostream>
#include<algorithm>
#include<queue>
#include<vector>
#include<map>
#include<set>
#include<string>
#include<stack>
#define ll long long
#define MAX 1050
#define INF INT_MAX
#define eps 1e-6
using namespace std;
int c[MAX][MAX];
int n;
int lowbit(int i){
return i&(-i);
}
void add(int i,int j,int d){
for (int u = i ; u <= n; u += lowbit(u))
for (int v = j; v <= n; v += lowbit(v))
c[u][v] += d;
}
int sum(int i, int j){
int s = 0;
for (int u = i; u > 0; u -= lowbit(u))
for (int v = j; v > 0; v -= lowbit(v))
s += c[u][v];
return s;
}
int main(){
int cas;
while (scanf("%d",&cas) != EOF){
int x,y,u,v,d;
if (cas == 0){
scanf("%d",&n);
memset(c,0,sizeof(c));
}
if (cas == 1){
scanf("%d%d%d",&u,&v,&d);
u += 1;
v += 1;
add(u,v,d);
}
if (cas == 2){
scanf("%d%d%d%d",&x,&y,&u,&v);
x += 1;
y += 1;
u += 1;
v += 1;
printf("%d\n",sum(u,v) - sum(x-1,v) - sum(u,y-1) + sum(x-1,y-1)); //注意如如何输出子矩阵的和
}
if (cas == 3) continue;
}
return 0;
}