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

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;
	}
}
时间: 2024-07-30 10:09:25

poj1330Nearest Common Ancestors以及讲解倍增法求lca的相关文章

倍增法求LCA

倍增法求LCA LCA(Least Common Ancestors)的意思是最近公共祖先,即在一棵树中,找出两节点最近的公共祖先. 倍增法是通过一个数组来实现直接找到一个节点的某个祖先,这样我们就可以在O(logn)的时间内求出求出任意节点的任意祖先. 然后先把两个节点中转化为深度相同的节点,然后一起向上递增,知道找到相同的节点,该节点就是这两个节点的最近公共祖先. 代码实现: 1 #include<cstdio> 2 #include<iostream> 3 #define N

【POJ1330】Nearest Common Ancestors(树链剖分求LCA)

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

倍增法求lca:暗的连锁

https://loj.ac/problem/10131 #include<bits/stdc++.h> using namespace std; struct node{ int to,next; }e[1000001]; int head[500000],num=0,N,n,m,ans; int grand[500001][20],depth[500001]; int f[100000],w[1000000]; inline void add(int x,int y) { e[++num]

用“倍增法”求最近公共祖先(LCA)

1.最近公共祖先:对于有根树T的两个结点u.v,最近公共祖先LCA(T,u,v)表示一个结点x,满足x是u.v的祖先且x的深度尽可能大.2.朴素算法:记录下每个节点的父亲,使节点u,v一步一步地向上找父亲,直到找到相同的“祖先”,即 是所求的答案,时间复杂度O(n).3.优化算法(倍增法):利用二进制的思想,想办法使一步一步向上搜变成以2^k地向上跳. 所以定义一个P[][]数组,使p[i][j]表示节点i的2^j倍祖先,因此p[i][0]即为i的父亲. 我们可以得到一个递推式p[i][j]=p

POJ1330Nearest Common Ancestors

题意 第一行输入T,有T组数据. 对于每组数据,给出一棵树,先输入n,然后n-1行,每行两个数a,b,表示a是b的父亲:第n行输入两个数A,B表示询问A和B的最近公共祖先. 题解 LCA模板题.建议先学学LCA 有两种方法,分别是Tarjan和倍增,这里说一说倍增.LCA_倍增是LCA的在线算法,时间和空间复杂度分别是O((n+q)log n)和O(n log n).对于这个算法,我们从最暴力的算法开始:①如果a和b深度不同,先把深度调浅,使他变得和浅的那个一样②现在已经保证了a和b的深度一样,

SPOJ COT2 Count on a tree II (树上莫队,倍增算法求LCA)

题意:给一个树图,每个点的点权(比如颜色编号),m个询问,每个询问是一个区间[a,b],图中两点之间唯一路径上有多少个不同点权(即多少种颜色).n<40000,m<100000. 思路:无意中看到树上莫队,只是拿来练练,没有想到这题的难点不在于树上莫队,而是判断LCA是否在两点之间的路径上的问题.耗时1天. 树上莫队的搞法就是: (1)DFS一次,对树进行分块,分成sqrt(n)块,每个点属于一个块.并记录每个点的DFS序. (2)将m个询问区间用所属块号作为第一关键字,DFS序作为第二关键字

LCA 在线倍增法 求最近公共祖先

第一步:建树  这个就不说了 第二部:分为两步  分别是深度预处理和祖先DP预处理 DP预处理: int i,j; for(j=1;(1<<j)<n;j++) for(int i=0;i<n;++i) if(fa[i][j]=-1) fa[i][j]=fa[fa[i][j-1]][j-1];/*DP处理出i的2^j祖先是谁*/ 深度预处理: 1 void dfs(int now,int from,int deepth) 2 { 3 deep[now]=deepth; 4 for(i

poj----1330Nearest Common Ancestors(简单LCA)

题目连接  http://poj.org/problem?id=1330 就是构建一棵树,然后问你两个节点之间最近的公共父节点是谁? 代码: 1 /*Source Code 2 Problem: 1330 User: huifeidmeng 3 Memory: 1232K Time: 63MS 4 Language: C++ Result: Accepted 5 6 Source Code 7 */ 8 #include<iostream> 9 #include<vector> 1

Poj1330Nearest Common Ancestors LCA

题意给一颗树,再给一个查询两点之间的最近公共祖先. #include<iostream> #include<cstdio> #include<cstring> #include<map> #include<vector> using namespace std; const int maxn = 111111; struct edge { int to; int next; }e[maxn * 10]; int len; int head[max