定义:对于有根树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 所在集合的祖先。

看一下算法描述吧:

View Code
 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

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

LCA tarjan 离线算法学习 
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

题意很简单,就是给一棵无向树,然后给出一条询问u,v,注意到题目描述中红色的部分,显然可以将树建立为有向的,这样可以方便的找出根节点是哪一个。
View Code
 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 }

 

分类:

技术点:

相关文章: