最近公共祖先(Least Common Ancestors) LCA
定义:对于有根树T的两个结点u、v,最近公共祖先LCA(T,u,v)表示一个结点x,满足x是u、v的祖先且x的深度尽可能大。另一种理解方式是把T理解为一个无向无环图,而LCA(T,u,v)即u到v的最短路上深度最小的点。
离线算法Tarjan:
利用并查集优越的时空复杂度,我们可以实现LCA问题的O(n+Q)算法,这里Q表示询问的次数。Tarjan算法基于深度优先搜索的框架,对于新搜索到 的一个结点,首先创建由这个结点构成的集合,再对当前结点的每一个子树进行搜索,每搜索完一棵子树,则可确定子树内的LCA询问都已解决。其他的LCA询 问的结果必然在这个子树之外,这时把子树所形成的集合与当前结点的集合合并,并将当前结点设为这个集合的祖先。之后继续搜索下一棵子树,直到当前结点的所 有子树搜索完。这时把当前结点也设为已被检查过的,同时可以处理有关当前结点的LCA询问,如果有一个从当前结点到结点v的询问,且v已被检查过,则由于 进行的是深度优先搜索,当前结点与v的最近公共祖先一定还没有被检查,而这个最近公共祖先的包涵v的子树一定已经搜索过了,那么这个最近公共祖先一定是v 所在集合的祖先。
看一下算法描述吧:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 //下面给出这个算法的伪代码描述: 2 3 //以下内容为程序代码: 4 5 LCA(u) 6 { 7 Make-Set(u) 8 ancestor[Find-Set(u)]=u 9 对于u的每一个孩子v 10 { 11 LCA(v) 12 Union(u) 13 ancestor[Find-Set(u)]=u 14 } 15 checked[u]=true 16 对于每个(u,v)属于P 17 { 18 if checked[v]=true 19 then { 20 回答u和v的最近公共祖先为 ancestor[Find-Set(v)] 21 } 22 } 23 }
http://poj.org/problem?id=1330
Nearest Common Ancestors
Time Limit: 1000MS | Memory Limit: 10000K | |
Total Submissions: 11132 | Accepted: 5924 |
Description
![](https://i-blog.csdnimg.cn/blog_migrate/a4cf40017a91cfbd4f81b13b267ec9ee.jpeg)
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
Output
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
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <cmath> 5 #include <algorithm> 6 using namespace std; 7 #define maxn 10005 8 typedef struct edge{ 9 int v; 10 edge *next; 11 edge(){next=NULL;} 12 }edge; 13 edge map[maxn],e[maxn<<1]; 14 edge query[maxn]; 15 int cnt,n; 16 int s,t; 17 int fa[maxn],anc[maxn]; 18 bool vis[maxn]; 19 int ans[maxn][maxn]; 20 int in[maxn]; 21 void addedge(int u,int v){ 22 edge *p=e+cnt++; 23 p->v=v; p->next=map[u].next; map[u].next=p; 24 } 25 void init(){ 26 cnt=0; 27 int a,b; 28 memset(map,0,sizeof(map)); 29 memset(query,0,sizeof(query)); 30 memset(anc,0,sizeof(anc)); 31 memset(in,0,sizeof(in)); 32 scanf("%d",&n); 33 for(int i=0;i<n-1;i++){ 34 scanf("%d%d",&a,&b); 35 addedge(a,b); 36 in[b]++; 37 } 38 scanf("%d%d",&s,&t); 39 edge *p=e+cnt++,*q=e+cnt++; 40 p->v=t; p->next=query[s].next; query[s].next=p; 41 q->v=s; q->next=query[t].next; query[t].next=q; 42 for(int i=0;i<=n;i++)fa[i]=i; 43 } 44 int find(int u){ 45 if(fa[u]!=u) fa[u]=find(fa[u]); 46 return fa[u]; 47 } 48 void munion(int u,int v){ 49 int a=find(u),b=find(v); 50 if(a<b) fa[b]=a; 51 else if(b<a) fa[a]=b; 52 } 53 void dfs(int u){ 54 anc[u]=u; 55 for(edge *e=map[u].next;e;e=e->next)if(!vis[e->v]){ 56 dfs(e->v); 57 munion(u,e->v); 58 anc[find(u)]=u; 59 } 60 vis[u]=1; 61 for(edge *e=query[u].next;e;e=e->next){ 62 int v=e->v; 63 if(vis[v]) { 64 ans[u][v]=ans[v][u]=anc[find(v)]; 65 } 66 } 67 } 68 int solve(){ 69 memset(vis,0,sizeof(vis)); 70 for(int i=1;i<=n;i++) 71 if(!in[i]) dfs(i); 72 return ans[s][t]; 73 } 74 int main() 75 { 76 freopen("in.txt","r",stdin); 77 int t; 78 scanf("%d",&t); 79 while(t--){ 80 init(); 81 printf("%d\n",solve()); 82 } 83 return 0; 84 }