传送门:http://poj.org/problem?id=3321
刚开始看到这题的时候,直接敲了一发模拟准备水过,结果TLE了,仔细想想确实是会超时的,如果变成一条直线的话,修改操作的复杂度就是O(N),妥妥超时。想了半天不知道怎么做,后来一查发现是树状数组,一时间没想出来怎么做,因为原来做的树状数组都是一条直线上操作,不知道怎么在树上建立树状数组。学习了一发别人的博客,才知道还有一种思路是根据树的dfs序来建立树状数组,先dfs一下,然后在dfs的时候编号。那么一个节点和他的全部子树的编号一定是连续的。所以这段连续区间就可以用来表示这个节点和他的子树。第一次看到这种做法,感觉非常吊炸天。
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <cstdlib>
#include <cctype>
#include <string>
#include <iostream>
#include <vector>
#include <map>
#include <queue>
#include <ctime>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
#define PB push_back
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define calm (l+r)>>1
const int INF=1e9+7;
const int maxn=100100;
struct EE{
int to,next;
EE(){}
EE(int to,int next):to(to),next(next){}
}edge[maxn];
int n,m,head[maxn],Ecnt,vis;
int st[maxn],ed[maxn],v[maxn];
int tree[maxn]={0};
inline void add(int s,int t){
edge[Ecnt]=EE(t,head[s]);
head[s]=Ecnt++;
}
void update(int x,int v){
while(x<=n){
tree[x]+=v;
x+=x&(-x);
}
}
int sum(int x){
int ans=0;
while(x){
ans+=tree[x];
x-=x&(-x);
}
return ans;
}
void build(int s){
st[s]=++vis;//st[s]表示当前这个节点
for(int i=head[s];~i;i=edge[i].next){
build(edge[i].to);
}
ed[s]=vis;//ed[x]表示它的所有子树的编号的最后一个,st[s]--ed[s]这段区间就可以表示这个节点与它的所有子树
}
int main(){
#ifdef LOCAL
freopen("input.txt","r",stdin);
#endif
scanf("%d",&n);
memset(head,-1,sizeof head);Ecnt=0;
for(int i=1;i<n;i++){
int a,b;scanf("%d%d",&a,&b);
add(a,b);
}
vis=0;
build(1);
for(int i=1;i<=n;i++){
v[i]=1;update(st[i],1);
//printf("%d %d\n",st[i],ed[i]);
}
scanf("%d",&m);
while(m--){
char op[2];
scanf("%s",op);
if(op[0]=='Q'){
int x;scanf("%d",&x);
printf("%d\n",sum(ed[x])-sum(st[x]-1));
}
else{
int x;scanf("%d",&x);
if(v[x]==1){
update(st[x],-1);
}
else{
update(st[x],1);
}
v[x]=1-v[x];
}
}
#ifdef LOCAL
printf("[Run in %.1fs]\n",(double)clock()/CLOCKS_PER_SEC);
#endif
return 0;
}