超级传送门:http://poj.org/problem?id=3321
题意:有一棵苹果树,每个分叉的交点或者末端刚开始都长有苹果,这些苹果可以被吃掉,也可以再长出来,但是每个位置上同时最多只有一个苹果。现在要查询指定的某棵子树上的苹果树。
分析:典型的树状数组求和问题,但是刚看题感觉很棘手,如何把一棵树映射到树状数组里?这里采用DFS改时间戳的方法,记两个数组begin和end,用begin[i]表示以i为根的子树遍历的第一个点,end[i]表示以i为根的子树遍历的最后一个点。
比如数据为:
5
1 2
2 5
2 4
1 3
那么begin[] = {1, 2, 5, 4, 3}, end[] = {5, 4, 5, 4, 3},下标从1开始。
对于每个点都对应一个区间(begin[i], end[i]),如果要改变点a的状态,只要update(begin[a]),要求该子树的苹果树,即getsum(end[a] ) - getsum(begin[a] - 1)。
注意此题用vector建树会TLE,所以我用数组建树,最大分支数只开了20,比较危险,如果卡到分支数很大的数据就悲剧了,还好AC了。最好还是自己用结构体和指针动态建树。
代码:
#include<cstdio>
#include<cstring>
#include<vector>
using namespace std;
const int maxn = 100005;
int c[maxn],a[maxn],begin[maxn],end[maxn],vis[maxn];
int t;
int tree[maxn][20],branchNum[maxn];
int n;
int lowbit(int t) {
return t & -t;
}
int getsum(int end) {
int ret = 0;
for (int i=end;i>=1;i-=lowbit(i)) {
ret += c[i];
}
return ret;
}
void update(int p,int val) {
for (int i=p;i<maxn;i+=lowbit(i)) {
c[i] += val;
}
}
void dfs(int p) {
if (vis[p]) return;
vis[p] = 1;
t++;
begin[p] = t;
for (int i=0;i<branchNum[p];i++) {
int x = tree[p][i];
dfs(x);
}
end[p] = t;
return;
}
int main () {
#ifndef ONLINE_JUDGE
freopen("1.txt","r",stdin);
#endif
int x,y,q;
char cmd[5];
while (scanf("%d",&n)>0 && n) {
memset(c,0,sizeof(c));
memset(vis,0,sizeof(vis));
memset(branchNum,0,sizeof(branchNum));
for (int i=1;i<=n;i++) {
memset(tree[i],0,sizeof(tree[i]));
a[i] = 1;
}
for (int i=1;i<=n-1;i++) {
scanf("%d%d",&x,&y);
tree[x][branchNum[x]++] = y;
}
t = 0; //时间戳
dfs(1);
for (int i=1;i<=n;i++) {
update(begin[i],1);
}
scanf("%d",&q);
while (q--) {
scanf("%s%d",cmd,&x);
if (cmd[0]=='Q') {
printf("%d\n",getsum(end[x])-getsum(begin[x]-1));
} else if (cmd[0]=='C') {
if (a[x]) {
update(begin[x],-1);
a[x] --;
} else {
update(begin[x],1);
a[x] ++;
}
}
}
}
return 0;
}