题目大意:
有一棵苹果树,树上有N个叉(编号为1~N,根的编号为1),它们通过分支连接。苹果在叉上生长,两个苹果不会在同一个叉上生长,一个新的苹果可以在空叉上长出来,对苹果有两种操作。
输入:
第1行一个整数N(N≤100,000),表示树中叉的数量。以下N-1行,每行都包含两个整数,表示叉u和叉v通过分支连接。下一行包含整数M(M≤100,000)表示有M个操作。每行一个操作:
C x表示改变叉上苹果状态(摘下一个苹果,或让长出一个新苹果)。
Q x表示查询x叉上方子树中的苹果数量,包括叉上的苹果。注意:开始时树上长满苹果。
输出:
对每个查询,都单行输出答案。
输入样例:
3
1 2
1 3
3
Q 1
C 2
Q 1
输出样例:
3
2
题解:
本题包含两种操作,一种是点更新,一种是查询以当前节点为根的子树的苹果数量。
对一棵树深度遍历,则记录遍历时当前节点进来和出去时的序号,两个序号之间的节点就是当前节点的子树节点。可以利用DFS序列将子树转换为序列,然后求解区间和。
点更新,求解区间和都可以用树状数组操作(区间和就是两个前缀和的差)。
本题中,摘掉一个苹果或新长出一个苹果,都会使父节点的个数改变,这也是使用树状数组的一个关键。
图解:
输入数据如下:
5
1 3
1 2
3 5
3 4
#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
const int maxn=1e5+10;
int N,q;
int c[maxn];
int a[maxn];//a[i]记录每个节点上有无苹果,默认都有
int L[maxn],R[maxn];
int head[maxn];
int cnt;
int dfn;
struct edge{
int u,v;
int next;
}E[2*maxn];
void adde(int u,int v){
E[++cnt].u=u;
E[cnt].v=v;
E[cnt].next=head[u];
head[u]=cnt;
}
int lowbit(int x){
return x&(-x);
}
void add(int i,int v){
for(;i<=N;i+=lowbit(i))
c[i]+=v;
}
int sum(int i){
int ans=0;
for(;i>0;i-=lowbit(i)){
ans+=c[i];
}
return ans;
}
void init(){
memset(c,0,sizeof(c));
memset(L,0,sizeof(L));
memset(R,0,sizeof(R));
memset(head,0,sizeof(head));
cnt=0;
dfn=0;
for(int i=1;i<=N;i++){
a[i]=1;
add(i,1);
}
//测试用
//for(int i=1;i<=N;i++) printf("%d ",c[i]);
}
//求出树的dfs序,即先序遍历,则一个子树的所有结点对应dfs序上连续的一段
void dfs(int u,int fa){
L[u]=++dfn;
for(int i=head[u];i;i=E[i].next){
int v=E[i].v;
if(v==fa) continue;
dfs(v,u);
}
R[u]=dfn;
}/*记录每个节点的进入序号和出去序号,就知道子树有几个节点
但是默认每个节点都有一个苹果,默认节点的个数就是苹果的个数。
但该题节点上可以摘掉苹果,并且频繁改变节点状态,所以采用树状数组来统计
*/
int main(){
scanf("%d",&N);
int u,v;
init();
for(int i=1;i<N;i++){
scanf("%d%d",&u,&v);
adde(u,v);
}
dfs(1,1);
/*printf("测试dfs序列\n");
for(int i=1;i<=N;i++)//测试
printf("%d %d\n",L[i],R[i]);
printf("\n");*/
scanf("%d",&q);
char op[10];
for(int i=1;i<=q;i++){
scanf("%s %d",op,&v);
/*用接收字符串的方式,避免多余的空格和换行符带来的困扰,
也可以使用 scanf(" %c%d", &op, &v);用空格屏蔽换行符,第一次见到 */
if(op[0]=='C'){
if(a[L[v]])
add(L[v],-1);
else
add(L[v],1);
a[L[v]]^=1;
}
else{
int s1=sum(R[v]);
int s2=sum(L[v]-1);
printf("%d\n",s1-s2);
}
}
return 0;
}