题目链接:http://poj.org/problem?id=1330
最近公共祖先模板题
Tarjan离线算法 :
读取所有询问后保存并按照一定方式排列。该算法主要采取深度优先搜索的方式,结合并查集,在搜索同时处理查询,大致过程如下:
搜索到节点u
访问标记u
对于u的所有子节点递归搜索,每搜索并处理完一个子节点后将其与当前节点合并,u为父节点
若有一个与u关联的询问 q(u, v) 并且 v 已经被访问过,则其lca为 find(v) - 即并查集父节点函数
为何lca为 find(v) :
如果有一个从当前结点到结点v的询问,且v已被检查过,则由于进行的是深度优先搜索,当前结点与v的最近公共祖先一定还没有被检查,而这个最近公共祖先的包涵v的子树一定已经搜索过了,那么这个最近公共祖先一定是v所在集合的祖先。
参考:http://dongxicheng.org/structure/lca-rmq/
实际代码编写中需要注意初始化,并查集合并时父节点是否一定等问题,细节处理要谨慎。
伪代码:
1 lca(node u){ 2 fa[u] = u; 3 vis[u] = 1; 4 for every son of u{ 5 lca(u.son) 6 union(u, u.son); 7 } 8 for every query of u { 9 if vis[q[u]] == 1 10 ans[u].push_back(find(q[u])) 11 } 12 }
题目AC代码:
1 const int inf = 0x3f3f3f3f; 2 const int maxn = 1e4 + 5; 3 struct Edge{ 4 int to, next, val; 5 }; 6 Edge edges[maxn * 2]; 7 int tot, head[maxn], vis[maxn]; 8 int n; 9 int fa[maxn]; 10 int qu, qv, ans; 11 int deg[maxn]; 12 13 void init(){ 14 memset(deg, 0, sizeof(deg)); 15 memset(edges, 0, sizeof(edges)); 16 memset(head, -1, sizeof(head)); 17 memset(vis, 0, sizeof(vis)); 18 for(int i = 0; i <= n; i++) fa[i] = i; 19 tot = 0; 20 } 21 void addEdge(int u, int v, int w){ 22 edges[tot].to = v; 23 edges[tot].val = w; 24 edges[tot].next = head[u]; 25 head[u] = tot++; 26 } 27 28 int find(int x){ 29 return (fa[x] == x)? x: fa[x] = find(fa[x]); 30 } 31 void unite(int x, int y){ 32 int fx = find(x), fy = find(y); 33 if(fx == fy) return; 34 fa[fy] = fx; 35 } 36 37 void lca(int u){ 38 fa[u] = u; 39 vis[u] = 1; 40 for(int i = head[u]; i != -1; i = edges[i].next){ 41 if(!vis[edges[i].to]) { 42 lca(edges[i].to); 43 unite(u, edges[i].to); 44 } 45 } 46 if(u == qu && vis[qv] == 1){ 47 ans = find(qv); 48 } 49 if(u == qv && vis[qu] == 1){ 50 ans = find(qu); 51 } 52 } 53 54 55 int main(){ 56 int T; 57 scanf("%d", &T); 58 while(T--){ 59 scanf("%d", &n); 60 init(); 61 for(int i = 0; i < n - 1; i++){ 62 int from, to; 63 scanf("%d %d", &from, &to); 64 deg[to]++; 65 addEdge(from, to, 1); 66 addEdge(to, from, 1); 67 } 68 int root = 1; 69 for(int i = 1; i <= n; i++) if(deg[i] == 0) root = i; 70 scanf("%d %d", &qu, &qv); 71 lca(root); 72 printf("%d\n", ans); 73 } 74 }
题目:
earest Common Ancestors
Time Limit: 1000MS | Memory Limit: 10000K | |
Total Submissions: 29509 | Accepted: 15069 |
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