题目大意:
有一棵苹果树,有n个节点,根节点为1,最开始每个节点上都有一个苹果。
有连个操作
(C) 如果节点X上有一个苹果,拿走这个苹果;如果X上没有苹果,这个节点立刻长出一个苹果
(Q) 求以X根的子树(包括X)一共有多少个苹果。
这题主要运用了树状数组更新时候的思维。
首先按照原树在树状数组上建一棵树,使得原树的子节点紧跟在根节点之前,并记录以该节点为根的子树的节点的数目son[x] 以及 自己对应在树状数组上的位置pos[x] 。
那么每次更新的时候以该节点在树状数组的位置往上更新 +1 或者 -1,至于询问的时候, 因为记录了自己在树状数组上的位置pos[x]以及 son[x], 那么pos[x]-son[x] 必定为第一个不在以x为根的子树中,query(pos[x]) - query( pos[x] -son[x] ) 即为所求。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define N 100010
int ip,iq;
bool flag[N]; //标记是否被采摘过,0表示没被采摘过,1表示已经被采摘过
int son[N]; //记录以X为根的子树的节点数
int num[N]; //树状数组
int head[N]; //原树建树时候用的,链式存储
int pos[N]; //原节点对应在树状数组上的位置
int n;
struct
{
int next;
int to;
}edge[N<<1];
void addedge(int u,int v)
{
edge[ip].next=head[u];
edge[ip].to=v;
head[u]=ip++;
}
void build(int pre,int pos1) //原树建树
{
++son[pos1];
for(int p=head[pos1];p!=-1;p=edge[p].next)
{
if(edge[p].to!=pre)
{
build(pos1,edge[p].to);
son[pos1]+=son[edge[p].to];
}
}
pos[pos1]=iq++; // 标记原树的节点在树状数组的位置
}
int lowbit(int x)
{
return x&-x;
}
void updata(int x,int v)
{
while(x<=n)
{
num[x]+=v;
x+=lowbit(x);
}
}
int query(int x)
{
int ret=0;
while(x>0)
{
ret+=num[x];
x-=lowbit(x);
}
return ret;
}
int main()
{int m;
char o[5];
int x,y;
while(cin>>n)
{
memset(head,-1,sizeof(head));
memset(num,0,sizeof(num));
memset(flag,0,sizeof(flag));
memset(son,0,sizeof(son));
ip=0;
iq=1;
for(int i=1;i<n;i++)
{
scanf("%d%d",&x,&y);
addedge(x,y);
addedge(y,x);
}
build(0,1);
cin>>m;
for(int i=1;i<=n;i++) updata(i,1);
while(m--)
{
scanf("%s",o);
if(o[0]=='C')
{
scanf("%d",&x);
if(!flag[x])
{
updata(pos[x],-1);
flag[x]^=1;
}
else
{
flag[x]^=1;
updata(pos[x],1);
}
}
else
{
scanf("%d",&x);
printf("%d\n",query(pos[x])-query(pos[x]-son[x]));
}
}
}
return 0;
}