#1067: Recent public ancestor · TwoTime limit:10000msSingle Point time limit:1000msMemory Limit:256MB
Describe
Last said, small hi and small ho with very poor--or rough means of the cottage out of a magical site, this site can calculate the two people of all the common ancestor generational the lowest one is who. Far away in the United States, they used some wonderful technology to get information about many people in the country and set up a small website to deal with requests from all directions.
But as far as we can imagine ... Such a simple algorithm does not support a very large amount of traffic, so there are two options in front of Little Hi and Little ho:
One is to buy more expensive servers to meet demand by improving computer performance-but little Hi and Little ho don't have that much money, and the second is to improve their algorithms to meet demand by improving the utilization of computer performance-an idea that sounds more plausible.
So for the smooth operation of their first online product, little Hi decided to train the little ho urgently--to modify their algorithms well.
And in order to better explain the problem to small ho, little hi has abstracted this question into this way: Suppose now little ho now knows the N-to-father-son relationship--the name of fathers and Sons, and that N for all involved in a parent-child relationship has a common ancestor (this ancestor appears in the N-parent relationship), He needs to ask a few questions about little Hi-the name of two people per question (the names of the two people in the previous parent-child relationship), and tell Little hi who is the lowest generational in all the common ancestors of these two people?
input
Each test point (input file) has and has only one set of test data.
The 1th behavior of each set of test data is an integer n, meaning as described earlier.
Each group of test data in line 2~n+1, each line describes a pair of parent-child relationship, where the i+1 behavior of two a string of uppercase and lowercase letters Father_i, son_i, respectively, the name of the father and the son.
The n+2 behavior of each set of test data is an integer m, which indicates the number of times a small hi has been asked.
Each group of test data in line n+3~n+m+2, each line describes a query, where the n+i+2 behavior of two characters of uppercase and lowercase letters name1_i, name2_i, respectively, a small hi asked two names.
For 100% of the data, to satisfy the n<=10^5,m<=10^5, and all the characters involved in the data does not exist two names of the same person (that is, the name of the only one identified), all of the names of the queries have been described in the previous N-parent-child relationship, The first name to be identified is the common ancestor of all other people .
Output
For each set of test data, for each small hi query, according to the order in the input, each output line, indicating the results of the queries: all of their common ancestors generational the lowest of a person's name.
-
Sample input
4Adam Samsam joeysam michealadam kevin3sam samadam sammicheal Kevin
Sample output
Samadamadam
Analysis: Bare LCA off-line algorithm. Leave it as a template.
Transferred from: http://taop.marchtea.com/04.04.html
The Tarjan algorithm (named after the discovery Robert Tarjan) is an algorithm for finding strong connected components in a graph. The basic idea of the algorithm is: Select a node to start the depth-first search DFS (if there are still non-visited nodes after the end of the depth-first search, then choose one point from the other). Nodes that have been visited during the search are no longer accessible. Several subtrees of the search tree make up the strong connected component of the graph.
For the LCA problem we are going to solve, it is: for a newly-found node u, first create a set composed of U, and then search each subtree of u, each time we search for a subtrees tree, the nearest public ancestor of all the nodes in the subtree is U.
Cite an example, such as (a node of different colors equals a different set):
Assuming that you have traversed 10 of the child, to handle the request about 10, take the root node to the currently traversing node is the path to the critical path, that is, 1-3-8-10, the collection's ancestor is the key path on the nearest point of the distance collection.
Like what:
- The 1,2,5,6 is a set of 1 ancestors, a set midpoint and a 10 LCA of 1
- The 3,7 is a set of 3 ancestors, a set midpoint and a 10 LCA of 3
- The 8,9,11 is a set of 8 ancestors, a set midpoint and a 10 LCA of 8
- The 10,12 is a set of 10 ancestors, a set midpoint and a 10 LCA of 10
The conclusion is that LCA (U,V) is the closest point on the path from root to u to node v.
But the key is how does the Tarjan algorithm come to mind? Given, whether you can see it: from the node 1 of the left and right sub-tree, whichever node, set to U, V, the two arbitrary nodes of U, V of the recent public ancestor are 1.
Here, we can be informed that: if two nodes u, v are distributed in the left and right sub-tree of a node T, then this node T is the nearest common ancestor of U and v. Further, given that a node itself is the case of LCA, it is learned that:
- If a node t is one of the ancestors of two nodes U, V, and the two nodes are not distributed in a subtrees tree of the node T, but in the Zuozi and right subtree of the node T, then the node T is the nearest common ancestor of the two nodes U and v.
This theorem is the basis of the Tarjan algorithm.
If more recent public ancestors of any two nodes are required, it is equivalent to a bulk query. In the case of many groups of queries, it may be possible to identify an LCA first. For example, the root node 1, and then to check all inquiries, to see if the theorem is satisfied, not satisfied with the neglect, satisfaction on the assignment, all finished, and then to assume that the number 2nd node is LCA, and then go to visit again.
But this method needs to determine whether a node is in the Zuozi, or the right subtree, or not, can only traverse a tree, and the cost of multiple traversal is too big, so we need to find a better way. This leads to the following Tarjan algorithm, that is, each node only traverse once, how to do it, please see below to explain.
The Tarjan algorithm flow is:
Procedure dfs(u); begin 设置u号节点的祖先为u 若u的左子树不为空,dfs(u - 左子树); 若u的右子树不为空,dfs(u - 右子树); 访问每一条与u相关的询问u、v -若v已经被访问过,则输出v当前的祖先t(t即u,v的LCA) 标记u为已经访问,将所有u的孩子包括u本身的祖先改为u的父亲 end
Ordinary Dfs can not directly solve the LCA problem, so the principle of Tarjan algorithm is DFS + and check set, it each time two node pairs of the recent common ancestor of the query to save, and then DFS update once. Thus, the time complexity of the algorithm can be reduced to O (n+q), where n is the data size and Q is the number of queries, using and checking the superior space-time complexity.
i) access to the Zuozi 1
STEP 1: Starting from root node 1, Access nodes 1, 2, 3.
STEP 2:2 left subtree node 3 access complete
STEP 3: Start accessing Nodes 4, 5, 6 in the right subtree of 2
Node 5 in the left subtree of STEP 4:4 has been accessed.
STEP 5: Start Access to node 6 of the right subtree of 4
STEP 6: Node 4 of the left and right subtree are all visited, so 4, 5, 6 of any two nodes of the LCA are 4
STEP 7:2 of the Zuozi, right subtree have been visited, so 2, 3, 4, 5, 6 any two nodes of the LCA are 2
As mentioned above: to this step7, when access to the end point 2 of Zuozi (3), and the right subtree (4, 5, 6), Node 2, 3, 4, 5, 6 of these 5 nodes, any two nodes of the nearest public ancestor are 2.
II) access to the right sub-tree of 1
STEP 8:1 Zuozi Access completed, start Access to 1 right subtree
STEP 9: Start accessing Nodes 7, 8 in the right subtree of 1
STEP 10
STEP 11
Nodes 7 and 8 in the right subtree of STEP 12:1 have been accessed.
When this step12 is reached, the most recent public ancestor of 5 nodes (2, 3, 4, 6, 7), and the right subtree (8, 2) of the nodes 3, 4, 5, 6, 7, 8, 7 are all two, after accessing the 1 Zuozi.
STEP 13:1 Zuozi and right subtree are all accessed.
From the above example, we can see that using this Tarjan algorithm can solve our LCA problem.
Title Link: http://hihocoder.com/problemset/problem/1067
Code Listing:
#include <map> #include <set> #include <queue> #include <stack> #include <cmath> #include <cstdio> #include <string> #include <cstring> #include <iostream> #include <algorithm> Using namespace Std;typedef long long ll;const int MAXN = 1e5 + 5;struct edge{int v,id; Edge (int v,int id) {This, v = v; This, id = ID; }};int n,m,id,root;string Name1,name2;bool hasfa[maxn];map<string,int>idx;string str[maxn];vector<int> Graph[maxn];int father[maxn];int color[maxn],ans[maxn];vector<edge>edge[maxn];int Find (int x) {return x!= FATHER[X]? Father[x]=find (Father[x]): father[x]; }void Init () {for (int i=0;i<maxn;i++) {graph[i].clear (); Edge[i].clear (); Father[i]=i; } memset (ans,0,sizeof (ans)); memset (color,0,sizeof (color)); memset (hasfa,false,sizeof (HASFA)); Idx.clear (); Id=0;} int Get_idx (string name) {if (Idx.count (name)) return Idx[name]; Idx[name]=++id; Str[id]=name; return ID;} void input () {scanf ("%d", &n); for (int i=0;i<n;i++) {cin>>name1>>name2; int Idx1=get_idx (NAME1); int Idx2=get_idx (name2); Graph[idx1].push_back (IDX2); Hasfa[idx2]=true; } scanf ("%d", &m); for (int i=1;i<=m;i++) {cin>>name1>>name2; int Idx1=get_idx (NAME1); int Idx2=get_idx (name2); Edge[idx1].push_back (Edge (idx2,i)); Edge[idx2].push_back (Edge (idx1,i)); }}void Tarjan (int u) {color[u]=1; for (int i=0;i<edge[u].size (); i++) {int id=edge[u][i].id; if (Ans[id]) continue; int v=edge[u][i].v; if (color[v]==0) continue; if (color[v]==1) ans[id]=v; if (color[v]==2) Ans[id]=find (v); } for (int i=0;i<graph[u].size (); i++) {int vv=graph[u][i]; Tarjan (VV); color[vv]=2; Father[vv]=u; }}void solve () {for (int i=1;i<=n;i++) if (!hasfa[i]) root=i; Tarjan (root); For (int i=1;i<=m;i++) Cout<<str[ans[i]]<<endl;} int main () {init (); Input (); Solve (); return 0;}
Copyright NOTICE: This article for Bo Master original article, without Bo Master permission not reproduced.
Hihocoder_#1067_ recent public ancestor • Two (LCA template)