POJ3321:Apple Tree

点击打开题目链接


Apple Tree

Time Limit: 2000MS Memory Limit: 65536K
Total Submissions: 16270 Accepted: 4872

Description

There is an apple tree outside of kaka's house. Every autumn, a lot of apples will grow in the tree. Kaka likes apple very much, so he has been carefully nurturing the big apple tree.

The tree has N forks which are connected by branches. Kaka numbers the forks by 1 to N and the root is always numbered by 1. Apples will grow on the forks and two apple won't grow on the same fork. kaka wants to know how many apples are there in a sub-tree, for his study of the produce ability of the apple tree.

The trouble is that a new apple may grow on an empty fork some time and kaka may pick an apple from the tree for his dessert. Can you help kaka?

Input

The first line contains an integer N (N ≤ 100,000) , which is the number of the forks in the tree.
The following N - 1 lines each contain two integers u and v, which means fork u and fork v are connected by a branch.
The next line contains an integer M (M ≤ 100,000).
The following M lines each contain a message which is either
"x" which means the existence of the apple on fork x has been changed. i.e. if there is an apple on the fork, then Kaka pick it; otherwise a new apple has grown on the empty fork.
or
"x" which means an inquiry for the number of apples in the sub-tree above the fork x, including the apple (if exists) on the fork x
Note the tree is full of apples at the beginning

Output

For every inquiry, output the correspond answer per line.

Sample Input

3
1 2
1 3
3
Q 1
C 2
Q 1

Sample Output

3
2

Source

POJ Monthly--2007.08.05, Huang, Jinsong


=====================================题目大意=====================================


卡卡家外有一棵有着N个叉的苹果树,每个叉都能且最多只能长出一个苹果,此外每个叉都有一个编号,根的编号总是1。

现在告诉你哪些叉是相连的,请你帮助卡卡完成M个对苹果树的操作:

C X表示摘取(有苹果时)或长出(没苹果时)第X个叉的苹果。

Q X表示计算以第X个叉为根的子树上苹果的个数(包括第X个叉)。

注意一开始每个叉上都长有苹果。


=====================================算法分析=====================================


多次查询和修改,很容易想到用线段树(树状数组)。

虽然数据本就是以树的形式给出的,但由于给出的苹果树不一定是二叉树,所以需要对苹果树的所有节点重新建树以方便区间查询

这样用新树来完成C X操作显然是没有任何问题的。

但是用新树来完成Q X操作就有点麻烦了,因为这需要得知X及其所有子节点在新树中的编号

而且这些编号还必须是连续的而不能是离散的,否则Q X的实现几乎不可想象。

幸好只需以DFS遍历的顺序给原树的各个节点依次编号就可以做到这一点(想一下DFS递归遍历的特点,没想明白参见下面代码中

的DFS函数)

于是对原树中的每个节点X便可记录下其自身在新树中的编号TreeL[X]及其子节点在新树中的最大编号TreeR[X]。

这样在执行C X操作的时候,新树中的目标节点编号便是TreeL[X]。

在执行Q X的时候新树中的目标节点区间便是[TreeL[X],TreeR[X]]。

而原树中节点X上是否有苹果,可以通过Q(TreeL[X])-Q(TreeL[X]-1)的值来判断,我是开了一个数组ExisApple[]来辅助判断的。


另外本题有一个莫名其妙的天坑:

保存边的时候用容器数组“vector<int> E[MAXN]”会TLE(运气好的话也能擦时限AC)。

保存边的时候用双重容器“vector< vector<int> > E”就无压力AC。。。暂时还没弄懂!


=======================================代码=======================================


一、线段树。




#include<vector>
#include<cstdio>
#include<cstring>

using namespace std;

#define LSON(N)      ((N)<<1  )               
#define RSON(N)      ((N)<<1|1)                    
#define MID(L,R)     (((L)+(R))>>1)    

const int MAXN=100005;

int N,M,CntFork,TreeL[MAXN],TreeR[MAXN],SegmTree[MAXN<<2];

bool ExisApple[MAXN];

//不可改为vector<int> E[MAXN]
vector< vector<int> > E(MAXN);     

void DFS(int Index)
{
	TreeL[Index]=CntFork;
	for(int i=0;i<E[Index].size();++i)
	{
		++CntFork;
		DFS(E[Index][i]);
	}
	TreeR[Index]=CntFork;
}

void Insert(int InsID,int InsVal,int L,int R,int N)
{
	SegmTree[N]+=InsVal;
	if(L!=R)
	{
		int M=MID(L,R);
		if(InsID<=M) { Insert(InsID,InsVal,L,M,LSON(N)); }
		else { Insert(InsID,InsVal,M+1,R,RSON(N)); }
	}
}

int Query(int QueL,int QueR,int L,int R,int N)
{
	if(QueR<L||R<QueL) { return 0; }
	if(QueL<=L&&R<=QueR) { return SegmTree[N]; }
	int M=MID(L,R);
	return Query(QueL,QueR,L,M,LSON(N))+Query(QueL,QueR,M+1,R,RSON(N));
}
	
bool InitData()
{
	memset(SegmTree,0,sizeof(SegmTree));
	for(int i=1;i<=N;++i)
	{
		ExisApple[i]=1;
		Insert(i,1,1,N,1);
		E[i].clear();
	}
	return true;
}

int main()
{
	while(scanf("%d",&N)==1)
	{		
		InitData();
		for(int i=1;i<N;++i)
		{
			int u,v;
			scanf("%d%d",&u,&v);
			E[u].push_back(v);
		}
		CntFork=1;	
		DFS(1);
		scanf("%d",&M);	
		while(M--)
		{
			int x;  char ch;
			scanf("%*c%c%d",&ch,&x);
			if(ch=='Q') { printf("%d\n",Query(TreeL[x],TreeR[x],1,N,1)); }
			else { ExisApple[x]^=1;  Insert(TreeL[x],(ExisApple[x]?1:-1),1,N,1); }
		}
	}
	return 0;
}


二、树状数组。




#include<vector>
#include<cstdio>
#include<cstring>

using namespace std;

#define LOWBIT(N) ((N)&(-(N)))

const int MAXN=100005;

int N,M,CntFork,TreeL[MAXN],TreeR[MAXN],BinTree[MAXN<<2];

bool ExisApple[MAXN];

//不可改为vector<int> E[MAXN]
vector< vector<int> > E(MAXN);     

void DFS(int Index)
{
	TreeL[Index]=CntFork;
	for(int i=0;i<E[Index].size();++i)
	{
		++CntFork;
		DFS(E[Index][i]);
	}
	TreeR[Index]=CntFork;
}

void UpDate(int UpID,int UpVal)
{
	while(UpID<=N)
	{
		BinTree[UpID]+=UpVal;
		UpID+=LOWBIT(UpID);
	}
}

int Query(int QueID)
{
	int sum=0;
	while(QueID>=1)
	{
		sum+=BinTree[QueID];
		QueID-=LOWBIT(QueID);
	}
	return sum;
}
	
bool InitData()
{
	memset(BinTree,0,sizeof(BinTree));
	for(int i=1;i<=N;++i)
	{
		ExisApple[i]=1;
		UpDate(i,1);
		E[i].clear();
	}
	return true;
}

int main()
{
	while(scanf("%d",&N)==1)
	{		
		InitData();
		for(int i=1;i<N;++i)
		{
			int u,v;
			scanf("%d%d",&u,&v);
			E[u].push_back(v);
		}
		CntFork=1;	
		DFS(1);
		scanf("%d",&M);	
		while(M--)
		{
			int x;  char ch;
			scanf("%*c%c%d",&ch,&x);
			if(ch=='Q') { printf("%d\n",Query(TreeR[x])-Query(TreeL[x]-1)); }
			else { ExisApple[x]^=1;  UpDate(TreeL[x],(ExisApple[x]?1:-1)); }
		}
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值