题目链接:POJ 3321
题意:一颗有n个分支的苹果树,根为1,每个分支只有一个苹果,给出n-1个分支的关系和给出m个操作,Q x表示询问x的子树(包括x)苹果的数量,C x表示若分支x上有苹果,则摘下来,若没有则会生出一个,输出每个询问的值。
题解:我们可以方便地计算出每个分支上的苹果,但是难点在于怎么求出子树上的苹果。
我们可以DFS遍历整个树,记录每个点遍历的时间戳,即访问的次序,第一次访问的时间戳(用first数组记录)和最后一次访问的时间戳(用last数组记录),这之间所有访问的点都是其子树上的点,所以我们可以以时间戳为下标生成一颗树状数组。查询时,计算first和last之间之和即可。更新时,改变first即可,因为第一个访问的必定是这个分支本身。
代码:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <cstring>
using namespace std;
const int MAX=100000+10;
int bit[MAX];
int N;
int first[MAX],last[MAX];
int dfs_cnt;
struct edge
{
int to,next;
};
edge E[MAX];
int head[MAX];
int cnt;
void addEdge(int u,int v)
{
E[cnt].to=v;
E[cnt].next=head[u];
head[u]=cnt++;
}
int sum(int x)
{
int s=0;
while(x>0)
{
s+=bit[x];
x-=(x&(-x));
}
return s;
}
void add(int x,int v)
{
while(x<=N)
{
bit[x]+=v;
x+=(x&(-x));
}
}
void dfs(int x)
{
first[x]=++dfs_cnt;
for(int i=head[x];i!=-1;i=E[i].next)
{
dfs(E[i].to);
}
last[x]=dfs_cnt;
return ;
}
int main()
{
int n,m;
while(scanf("%d",&n)==1)
{
memset(head,0xff,sizeof(head));
memset(E,0,sizeof(E));
cnt=0;
for(int i=0;i<n-1;i++)
{
int u,v;
scanf("%d%d",&u,&v);
addEdge(u,v);
}
N=n;
dfs_cnt=0;
dfs(1);
for(int i=1;i<=n;i++)
add(i,1);
scanf("%d",&m);
for(int i=0;i<m;i++)
{
getchar();
char op;
int x;
scanf("%c %d",&op,&x);
if(op=='Q')
{
printf("%d\n",sum(last[x])-sum(first[x]-1));
}
else if(op=='C')
{
if(sum(first[x])-sum(first[x]-1)) add(first[x],-1);
else add(first[x],1);
}
}
}
return 0;
}