1.题目链接。题目大意:一棵树上的每一个节点在初始的时候都长了一个苹果,然后有这样的两种操作:
(1)Q x:询问以x为根节点的子树上苹果的数量
(2)C x把标号为x除的状态改变。状态改变的意思就是原来这里有苹果,就变没,原来没有就长出来一个,保证每个节点最多一个苹果。
2.首先我们肯定没有办法离线所有的操作,然后看这种点修改,区间查询,马上就想到了树状数组了。但是我们怎么把树上的信息放在树状数组上维护呢?这是一个很重要的问题。其实这个问题并不难解决,我们可以对这棵树做一遍dfs,dfs的过程中对节点编号,这样每一颗子树都一定是一个连续的区间,区间的起点就是根节点,终点是子树里面编号最大的那个节点,所以在dfs回溯的过程中维护一下这个信息,那么我们就可以把一颗子树映射成数组上一段连续的区间,然后就是点修改,区间查询的基本操作了。
using namespace std;
const ll maxn = 1e5 + 5;
int n;
int trans[maxn];
vector< vector<int> >v(maxn);
int tree[maxn], apple[maxn], Next[maxn];
int cnt = 1;
inline int lb(int x)
{
return x & (-x);
}
void add(int x, int num)
{
for (int i = x; i <= n; i += lb(i))
tree[i] += num;
}
ll getsum(int x)
{
ll ans = 0;
for (int i = x; i > 0; i -= lb(i))
ans += tree[i];
return ans;
}
void dfs(int x)
{
trans[x] = cnt++;
for (int i = 0; i < v[x].size(); i++)
dfs(v[x][i]);
Next[trans[x]] = cnt - 1;
}
int main()
{
while (~scanf("%d", &n) && n)
{
int a, b;
cnt = 1;
memset(tree, 0, sizeof(tree));
memset(Next, 0, sizeof(Next));
memset(trans, 0, sizeof(trans));
for (int i = 1; i <= n; i++)
{
v[i].clear();
apple[i] = 1;
add(i, 1);
}
for (int i = 1; i <n; i++)
{
scanf("%d%d", &a, &b);
v[a].push_back(b);
}
dfs(1);
int T;
scanf("%d", &T);
while (T--)
{
char s;
int k;
//cin >> s >> k;
scanf(" %c %d", &s, &k);
int t = trans[k];
if (s == 'Q')
{
ll ans = getsum(Next[t]) - getsum(t - 1);
printf("%lld\n", ans);
}
else
{
if (apple[k] == 1)
add(t, -1);
else
add(t, 1);
apple[k] = !apple[k];
}
}
}
return 0;
}