简单入门的树状数组题目
对应典型的树状数组操作
对照白书的写法:
#include <stdio.h>
#include <string.h>
#define MAXN 50010
int a[MAXN+100], c[MAXN+100];
int lowbit(int x) {
return x&(-x);
}
int sum(int x) {
int sum = 0;
while(x > 0) {
sum += c[x];
x -= lowbit(x);
}
return sum;
}
void add(int x, int d) {
while(x <= MAXN) {
c[x] += d;
x += lowbit(x);
}
}
int main(void) {
int T, t, i, j, N;
char str[100];
scanf("%d", &T);
for(t=1; t<=T; ++t) {
scanf("%d", &N);
memset(c, 0, sizeof(c));
for(i=1; i<=N; ++i) {
scanf("%d", &a[i]);
for(j=i-lowbit(i)+1; j<=i; ++j)
c[i] += a[j];
}
printf("Case %d:\n", t);
while(scanf("%s", str) && strcmp(str, "End")) {
scanf("%d%d", &i, &j);
switch(str[0]) {
case 'A':
add(i, j); break;
case 'S':
add(i, -j); break;
case 'Q':
printf("%d\n", sum(j)-sum(i-1)); break;
}
}
}
}
看了别人的代码,a数组并不是必要的
可以在输入的过程中构造c数组,而不是等a数组输入完成后再构造c数组
代码如下:
#include <stdio.h>
#include <string.h>
#define MAXN 50010
int a, c[MAXN+100];
int lowbit(int x) {
return x&(-x);
}
int sum(int x) {
int sum = 0;
while(x > 0) {
sum += c[x];
x -= lowbit(x);
}
return sum;
}
void add(int x, int d) {
while(x <= MAXN) {
c[x] += d;
x += lowbit(x);
}
}
int main(void) {
int T, t, i, j, N;
char str[100];
scanf("%d", &T);
for(t=1; t<=T; ++t) {
scanf("%d", &N);
memset(c, 0, sizeof(c));
for(i=1; i<=N; ++i) {
scanf("%d", &a);
add(i, a);//等价于上面代码中c的构造公式
}
printf("Case %d:\n", t);
while(scanf("%s", str) && strcmp(str, "End")) {
scanf("%d%d", &i, &j);
switch(str[0]) {
case 'A':
add(i, j); break;
case 'S':
add(i, -j); break;
case 'Q':
printf("%d\n", sum(j)-sum(i-1)); break;
}
}
}
}
学习线段树之后觉得线段树较树状数组易于理解
下面是线段树的代码:
#include <cstdio>
#include <iostream>
#include <algorithm>
#define MAXN 50010
#define LL long long
using namespace std;
int sum[MAXN<<2];
void pushUp(int rt) {//当前根对应的值为左右孩子值之和
sum[rt] = sum[rt<<1]+sum[rt<<1|1];
}
void build(int l, int r, int rt) {//递归建树过程
if(l == r) {
scanf("%d", &sum[rt]);
return ;
}
int m = (l+r) >> 1;
build(l, m, rt<<1);
build(m+1, r, rt<<1|1);
pushUp(rt);
}
void update(int p, int add, int l, int r, int rt) {//单点更新,令p点对应值+add, l r表示左右边界, rt表示根节点
if(l == r) {
sum[rt] += add;
return ;
}
int m = (l+r) >> 1;
if(p <= m)
update(p, add, l, m, rt<<1);
else update(p, add, m+1, r, rt<<1|1);
pushUp(rt);
}
int query(int L, int R, int l, int r, int rt) {//统计L R之间的叶节点之和,l r表示左右边界, rt表示根节点
if(L<=l && r<=R)
return sum[rt];
int m = (l+r) >> 1;
int ret = 0;
if(L <= m)
ret += query(L, R, l, m, rt<<1);
if(R > m)
ret += query(L, R, m+1, r, rt<<1|1);
return ret;
}
int main(void) {
int T, n;
scanf("%d", &T);
for(int t=1; t<=T; ++t) {
printf("Case %d:\n", t);
scanf("%d", &n);
build(1, n, 1);
char op[10];
while(scanf("%s", op)) {
if(op[0] == 'E')
break;
int a, b;
scanf("%d%d", &a, &b);
if(op[0] == 'Q')
printf("%d\n", query(a, b, 1, n, 1));
else if(op[0] == 'S')
update(a, -b, 1, n, 1);
else update(a, b, 1, n, 1);
}
}
return 0;
}