一道很好的树状数组题目,题目的意思是给一树,刚开始每个节点有一个果子,有多次操作,每次操作若该点处有果子,则把果子摘了,否则该节点处添一个果子。每次询问以该节点为根的子树的果子数。题目的关键在于如何转化才能用树状数来解,比如我们要统计以i为根的子树的果数,能不能与树状数组中的Sum值联系起来呢?其实这样是可以的,如果重新编号,把该子树的节点的排在其左边,且知道最左边的节点的编号C1,该节点的编号为C2;那么其子树的果子数就为Sum(C2)-Sum(C1-1),这样问题就解决了。重新编号可以Dfs一次实现,分别求出每个点时间戳,C1以该节点的子树的开始记录也就是其子树中排在最左边的编号,C2表示该节点的编号。用vector超时了,自己重新学习写链表。
Memory: 8900K Time: 1000MS
Language: G++ Result: Accepted
Source Code
#include "stdio.h"
#include "string.h"
#define Inf 100005
struct Link{
int v;
Link *next;
}*List[Inf];
int n,a,b,r,cunt=1;
int C1[Inf],C2[Inf],C[Inf],S[Inf];
char str[3];
bool visited[Inf];
int lowbit(int x)
{
return x&(x^(x-1));
}
void Dfs(int c) //Dfs编号,记录每个节点开始值与结束值
{
C1[c]=cunt;
while(List[c])
{
int t=List[c]->v;
if(!visited[t])
{
visited[t]=true;
Dfs(t);
}
List[c]=List[c]->next;
}
C2[c]=cunt++;
}
void Add(int x,int t)
{
while(x<=n)
{
C[x]+=t;
x+=lowbit(x);
}
}
int Sum(int x)
{
int sum=0;
while(x>0)
{
sum+=C[x];
x-=lowbit(x);
}
return sum;
}
void Init()
{
Link *temp;
for(int i=1;i<n;i++)
{
scanf("%d%d",&a,&b);
temp=new Link;
temp->v=b;
temp->next=List[a];
List[a]=temp;
}
}
int main()
{
scanf("%d",&n);
Init();
Dfs(1);
for(int i=1;i<=n;i++)
{
Add(i,1);
S[i]=1;
}
scanf("%d",&r);
for(int k=1;k<=r;k++)
{
scanf("%s%d",str,&a);
if(str[0]=='C')
{
S[C2[a]]=!S[C2[a]];
if(!S[C2[a]])
Add(C2[a],-1);
else
Add(C2[a],1);
}
else
printf("%d/n",Sum(C2[a])-Sum(C1[a]-1));
}
return 0;
}