试了一个完全二叉树,貌似效率有点低:
#include <cstdio>
#include <cstring>
struct SegmentTreeNode{
int l, r, n;
} node[1 << (16 + 1) + 1];//2 ^ 16 = 65536 > 50000
void build(int l, int r, int index)
{
node[index].l = l;
node[index].r = r;
node[index].n = 0;
if(l == r) return;
build(l, (r + l) >> 1, index << 1);
build(((r + l) >> 1) + 1, r, (index << 1) + 1);
}
void addLeaf(int index, int n)
{
for(; index; index >>= 1) node[index].n += n;
}
int query(int l, int r, int index)
{
if(node[index].l == l && node[index].r == r) return node[index].n;
int m = (node[index].l + node[index].r) >> 1;
if(m >= r) return query(l, r, index << 1);
else if(m < l) return query(l, r,(index << 1) + 1);
else return query(l, m, index << 1) + query(m + 1, r, (index << 1) + 1);
}
int minInPower2(int num)
{
int n = 1;
for(; n < num; n <<= 1) ;
return n;
}
int main()
{
int t, test, n, m, i, v;
char cmd[8];
for(scanf("%d", &test), t = 1; t <= test; ++t){
printf("Case %d:\n", t);
//input point count and get the minimum number in power 2 larger than count
scanf("%d", &n);
m = minInPower2(n);
//build full binary segment tree
build(1, m, 1);
//init all leaves
for(i = 0; i < n; ++i){
scanf("%d", &v);
addLeaf(m + i, v);
}
//input operations
while(true){
scanf(" %s", cmd);
if(strcmp(cmd, "End") == 0) break;
scanf("%d %d", &i, &v);
if(strcmp(cmd, "Add") == 0) addLeaf(m + i - 1, v);
else if(strcmp(cmd, "Sub") == 0) addLeaf(m + i - 1, -v);
else printf("%d\n", query(i, v, 1));
}
}
return 0;
}
再试了一下非完全二叉树:
#include <cstdio>
#include <cstring>
#define MAX_RANGE 50000
struct SegmentTreeNode{
int l, r, n;
} node[MAX_RANGE * 3];
void build(int l, int r, int index)
{
node[index].l = l;
node[index].r = r;
node[index].n = 0;
if(l == r) return;
build(l, (r + l) >> 1, index << 1);
build(((r + l) >> 1) + 1, r, (index << 1) + 1);
}
void addLeaf(int leaf, int num, int index)
{
node[index].n += num;
if(node[index].l == node[index].r) return;
int m = (node[index].l + node[index].r) >> 1;
if(m >= leaf) addLeaf(leaf, num, index << 1);
else addLeaf(leaf, num, (index << 1) + 1);
}
int query(int l, int r, int index)
{
if(node[index].l == l && node[index].r == r) return node[index].n;
int m = (node[index].l + node[index].r) >> 1;
if(m >= r) return query(l, r, index << 1);
else if(m < l) return query(l, r,(index << 1) + 1);
else return query(l, m, index << 1) + query(m + 1, r, (index << 1) + 1);
}
int main()
{
int t, test, n, i, v;
char cmd[8];
for(scanf("%d", &test), t = 1; t <= test; ++t){
printf("Case %d:\n", t);
//input range and build tree
scanf("%d", &n);
build(1, n, 1);
//init all leaves
for(i = 1; i <= n; ++i){
scanf("%d", &v);
addLeaf(i, v, 1);
}
//input operations
while(true){
scanf(" %s", cmd);
if(strcmp(cmd, "End") == 0) break;
scanf("%d %d", &i, &v);
if(strcmp(cmd, "Add") == 0) addLeaf(i, v, 1);
else if(strcmp(cmd, "Sub") == 0) addLeaf(i, -v, 1);
else printf("%d\n", query(i, v, 1));
}
}
return 0;
}
貌似更慢了,目测是因为完全二叉树从下至上递推的更新区间比非完全二叉树从上至下递归的更新区间效率高些。
刚学了下树状数组,貌似树状数组的操作好简单的样子,而这一题简直就是为了树状数组而生的嘛:
#include <cstdio>
#include <cstring>
int n, C[50001] = {0};
inline int lowbit(int x){
return x & -x;
}
void add(int x, int d){
while(x <= n){
C[x] += d;
x += lowbit(x);
}
}
int sum(int x)
{
int res = 0;
while(x){
res += C[x];
x -= lowbit(x);
}
return res;
}
int main()
{
int t, test, i, x, d;
char cmd[8];
for(scanf("%d", &test), t = 1; t <= test; ++t){
printf("Case %d:\n", t);
scanf("%d", &n);
//initialize
memset(C + 1, 0, n << 2);
for(i = 1; i <= n; ++i){
scanf("%d", &d);
add(i, d);
}
//operations
while(scanf(" %s", cmd), strcmp(cmd, "End")){
scanf("%d %d", &x, &d);
if(!strcmp(cmd, "Add")) add(x, d);
else if(!strcmp(cmd, "Sub")) add(x, -d);
else printf("%d\n", sum(d) - sum(x-1));
}
}
return 0;
}
可以看到无论在时间、空间、还是代码量上,树状数组都完胜啊,果真是个好东西……