超級傳送門: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;
}