题目描述
解法:树状数组
首先使用树状数组是很明确的,准确来说是一个PUIQ模型(单点更新,区域查询)。
但是现在我们的输入不是一个数组了,而是一棵树,怎么办呢?
对一棵树进行深搜,然后将深搜的顺序重新标上号,然后放到一个数组中(即树状数组的线性表部分 a [ . . . ] a[... ] a[...]),记下每一个分支的起始点和终结点,然后就可以用树状数组了。
举个栗子吧,
那么当我们深搜的时候就可以在下面标上:
1 4 6 5 7 2 3 (广义表里面的东西,深搜结果)
1 2 3 4 5 6 7( a [ . . . ] a[... ] a[...]数组我们求和要用的)
s t a r t [ 1 ] = 1 e n d [ 1 ] = 7 start[1] = 1 \ \ \ end[1]=7 start[1]=1 end[1]=7代表1以上的树枝为1-7
s t a r t [ 2 ] = 6 e n d [ 2 ] = 7 start[2]= 6 \ \ \ end[2] = 7 start[2]=6 end[2]=7代表2以上的树枝是 3
找到每一个树枝在 a [ . . . ] a[... ] a[...]数组中的位置,这样我们就可以计算啦,当然实际编程中并不需要 a [ . . . ] a[... ] a[...]数组,只需要直接更新树状部分。
下面解释一下编码中一些问题,
编码中的树结构其实是有向图的邻接表,这里仅是根据题意采用树的说法。而dfs中没有显示的 v i s i t e d [ . . . ] visited[...] visited[...]数组,是因为我们将访问过的结点都赋予了新的id编号,该编号从1开始。根据题目意思,查询树枝 x x x上方子树中的苹果数量是包括 x x x上的苹果的(如果存在),因此在区域查询中应为 ( s t a r t [ x ] − 1 , e n d [ x ] ) (start[x]-1, end[x]) (start[x]−1,end[x]).
#include <iostream>
using namespace std;
int n, m, s, t, x, id = 0;
int c[100001], start[100001], _end[100001];
char cmd;
struct TreeNode{
int forkno;
TreeNode* next;
}tree[100001];
inline int min(int x, int y){return x<y?x:y;}
inline int lowbit(int x){return x&(-x);}
void add(int x, int v)
{
for(int i=x;i<=n;i+=lowbit(i))
c[i] += v;
}
int sum(int x)
{
int s = 0;
for(int i=x;i;i-=lowbit(i))
s += c[i];
return s;
}
void dfs(int pos)
{
start[pos] = ++id;
add(id, 1);
TreeNode* p = tree[pos].next;
while(p)
{
if(start[p->forkno]==0) dfs(p->forkno);
p = p->next;
}
_end[pos] = id;
}
int main()
{
scanf("%d", &n);
for(int i=1;i<n;i++)
{
scanf("%d%d", &s, &t);
TreeNode* p = new TreeNode;
p->forkno = t;
p->next = tree[s].next;
tree[s].next = p;
TreeNode* q = new TreeNode;
q->forkno = s;
q->next = tree[t].next;
tree[t].next = q;
}
dfs(1);
scanf("%d", &m);
for(int i=0;i<m;i++)
{
getchar();
scanf("%c%d", &cmd, &x);
if(cmd=='C')
{
int _sum = sum(start[x])-sum(start[x]-1);
if(_sum) add(start[x], -1);
else add(start[x], 1);
}
else
{
int _sum = sum(_end[x]) - sum(start[x]-1);
printf("%d\n", _sum);
}
}
return 0;
}