题意:给出一棵树,一开始每个节点都长着一个苹果,有两种操作,一种是改变某个节点苹果状态(之前有苹果改为无苹果,无苹果改为由苹果),另一种就是求一个节点连同其所有子节点的苹果总数。
难点:
如果利用树状数组或线段树,如何构造区间?
解题思路:
利用深度优先搜索把每个节点开始遍历和结束遍历的时间记录下来,因为在开始时间和结束时间之间遍历了这个节点的所有子孙节点,所以可以利用这两个数据构建一个从节点标记到时间的两个映射。在利用这个映射构建树状数组。利用树状数组统计苹果数量。
注意:由于在开始时间和结束时间之间两次访问了节点,所以利用树状数组计算出一个树枝上的数据后有除以2;
代码如下
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <vector>
#include <iostream>
using namespace std;
const int MAXN = 200004;
typedef vector<int> ONEDA;
vector<ONEDA> a(100000);
int num[MAXN], m, start[2 * MAXN], end[2 * MAXN], time;
bool hasa[MAXN];
void DFS(int s);
int query(int s);
void modify(int s, int add);
int main()
{
int x, y, i, n, ms;
scanf("%d", &n);
n--;
m = n;
while(n--){
scanf("%d%d", &x, &y);
a[x][y] = 1;
}
DFS(1);
for(i = 1; i <= time; i++)
{
hasa[i] = true;
num[i] = i & (-i);
}
scanf("%d", &ms);
char c;
int tar;
while(ms--){
scanf(" %c%d", &c, &tar);
if(c == 'C'){
hasa[start[tar]] = !hasa[start[tar]];
hasa[end[tar]] = !hasa[end[tar]];
if(hasa[start[tar]])
{
modify(start[tar], 1);
modify(end[tar], 1);
}
else
{
modify(start[tar], -1);
modify(end[tar], -1);
}
}
if(c == 'Q'){
int teme = query(end[tar]);
int tems = query(start[tar]-1);
printf("%d\n", (teme - tems) / 2);
}
}
return 0;
}
void DFS(int s)
{
start[s] =++time;
for(int i = 0; i < a[s].size(); i++)
{
DFS(a[s][i]);
}
end[s] = ++time;
}
void modify(int s, int add)
{
while(s <= time)
{
num[s] += add;
s += s & (-s);
}
}
int query(int s)
{
int sum = 0;
while(s > 0){
sum+=num[s];
s -= s & (-s);
}
return sum;
}