码迷,mamicode.com
首页 > 其他好文 > 详细

poj1330Nearest Common Ancestors以及讲解倍增法求lca

时间:2015-04-16 21:59:39      阅读:186      评论:0      收藏:0      [点我收藏+]

标签:

Time Limit: 1000MS   Memory Limit: 10000K
Total Submissions: 20487   Accepted: 10784

Description

A rooted tree is a well-known data structure in computer science and engineering. An example is shown below:

技术分享
In the figure, each node is labeled with an integer from {1, 2,...,16}. Node 8 is the root of the tree. Node x is an ancestor of node y if node x is in the path between the root and node y. For example, node 4 is an ancestor of node 16. Node 10 is also an ancestor of node 16. As a matter of fact, nodes 8, 4, 10, and 16 are the ancestors of node 16. Remember that a node is an ancestor of itself. Nodes 8, 4, 6, and 7 are the ancestors of node 7. A node x is called a common ancestor of two different nodes y and z if node x is an ancestor of node y and an ancestor of node z. Thus, nodes 8 and 4 are the common ancestors of nodes 16 and 7. A node x is called the nearest common ancestor of nodes y and z if x is a common ancestor of y and z and nearest to y and z among their common ancestors. Hence, the nearest common ancestor of nodes 16 and 7 is node 4. Node 4 is nearer to nodes 16 and 7 than node 8 is.

For other examples, the nearest common ancestor of nodes 2 and 3 is node 10, the nearest common ancestor of nodes 6 and 13 is node 8, and the nearest common ancestor of nodes 4 and 12 is node 4. In the last example, if y is an ancestor of z, then the nearest common ancestor of y and z is y.

Write a program that finds the nearest common ancestor of two distinct nodes in a tree.

Input

The input consists of T test cases. The number of test cases (T) is given in the first line of the input file. Each test case starts with a line containing an integer N , the number of nodes in a tree, 2<=N<=10,000. The nodes are labeled with integers 1, 2,..., N. Each of the next N -1 lines contains a pair of integers that represent an edge --the first integer is the parent node of the second integer. Note that a tree with N nodes has exactly N - 1 edges. The last line of each test case contains two distinct integers whose nearest common ancestor is to be computed.

Output

Print exactly one line for each test case. The line should contain the integer that is the nearest common ancestor.

Sample Input

2
16
1 14
8 5
10 16
5 9
4 6
8 4
4 10
1 13
6 15
10 11
6 7
10 2
16 3
8 1
16 12
16 7
5
2 3
3 4
3 1
1 5
3 5

Sample Output

4
3

Source

Taejon 2002

倍增法大概就是说把一个O(n)的问题转换成log(n)的问题。
lca就是求树上两个结点的最近公共祖先
倍增法求lca是这样做的:
首先预处理出两个数组
pr[i][j]:结点i的第2^j个祖先结点
dth[i]:结点i在树中的深度
这个可用递推跟搜索得出,递推式是这样子的pr[i][j]=pr[pr[i][j-1]][j-1],
怎么来的呢?i的第2^j个祖先也就是i的第2^(j-1)个祖先的第2^(j-1)个祖先,
因为2^(j-1)+2^(j-1)=2*2^(j-1)=2^j

当求one,two这两个结点的lca时,
首先将他们用倍增法提高到同一深度,怎么做呢?
为了方便描述,保证dth[one]>dth[two],
算出它们之间的深度差re=dth[one]-dth[two],
从re的二进制的最低位开始走到最高位,
若是遇到1,假设此时是第i位,则one-2^i,
结束时,one就跟two处于同一深度了,这里是巧妙的利用了二进制的加减法运算

我们再同时提高one,two的深度,假如总共有n个结点,
我们枚举它们的第2^i个祖先,从log(n)开始枚举到0
当pr[one][i]!=pr[two][i],则one=pre[one][i],two=pre[two][i],
那么最后,pre[one][0]就是它们的lca
为什么可以这样做呢?很显然若pr[one][i]==pr[two][i]
则,pr[one][i]是one,two的公共祖先,但不一定是最近的,所以不要管,
若pr[one][i]!=pr[two][i]那么pr[one][i]跟pr[two][i]就一定还在lca的子树里,
继续往上跑就行了,而且每次跑完,显然离lca就越近,i肯定比之前要小,
所以直接枚举到0就行了,到最后one跟two肯定都会跑到lca的儿子结点,于是
此时,one跟two的第2^0个祖先,也就是它们的父亲就是lca啦



这个题是求出,以入度为0的结点为根的树里,一对结点的lca
#include<map>
#include<string>
#include<cstring>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<queue>
#include<vector>
#include<iostream>
#include<algorithm>
#include<bitset>
#include<climits>
#include<list>
#include<iomanip>
#include<stack>
#include<set>
using namespace std;
int head[10010],tail;
struct Edge
{
	int to,next;
}edge[20010];
void add(int from,int to)
{
	edge[tail].to=to;
	edge[tail].next=head[from];
	head[from]=tail++;
}
int pr[10010][20],dth[10010];
void bfs(int st)
{
	queue<int>qq;
	qq.push(st);
	memset(pr,0,sizeof(pr));
	dth[st]=1;
	while(qq.size())
	{
		int from=qq.front();
		qq.pop();
		for(int i=head[from];i!=-1;i=edge[i].next)
		{
			int to=edge[i].to;
			if(to!=pr[from][0])
			{
				dth[to]=dth[from]+1;
				pr[to][0]=from;
				for(int i=1;(1<<i)<=dth[from];i++)
					pr[to][i]=pr[pr[to][i-1]][i-1];
				qq.push(to);
			}
		}
	}
}
int lca(int one,int two)
{
	if(dth[one]<dth[two])
		swap(one,two);
	int re=dth[one]-dth[two];
	for(int i=0;(re>>i)!=0;i++)
		if((re>>i)&1)
			one=pr[one][i];
	if(one!=two)
	{
		for(int i=15;i>-1;i--)
			if(pr[one][i]!=pr[two][i])
			{
				one=pr[one][i];
				two=pr[two][i];
			}
		one=pr[one][0];
	}
	return one;
}
bool flag[10010];
int main()
{
	int T;
	cin>>T;
	while(T--)
	{
		int n;
		cin>>n;
		tail=0;
		memset(head,-1,sizeof(head));
		memset(flag,0,sizeof(flag));
		for(int i=1;i<n;i++)
		{
			int from,to;
			cin>>from>>to;
			add(from,to);
			add(to,from);
			flag[to]=1;
		}
		for(int i=1;i<=n;i++)
			if(!flag[i])
			{
				bfs(i);
				break;
			}
		int one,two;
		cin>>one>>two;
		cout<<lca(one,two)<<endl;
	}
}

 

poj1330Nearest Common Ancestors以及讲解倍增法求lca

标签:

原文地址:http://blog.csdn.net/stl112514/article/details/45080123

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!