#include<iostream>
#include<cstdio>
#include<vector>
using namespace std;
int n;
int father[105];                            //每个节点的父节点  
int rnk[105];                               //树中节点的个数  
int ancestor[105];                          //已访问节点集合的祖先  

void initSet() {
	for (int i = 0; i<n; i++) {
		father[i] = i;                      //初始化时,所在子树的祖先就是自己  
		rnk[i] = 1;                         //所在树的深度为0  
	}
}

int findSet(int x) {
	if (x != father[x])
		father[x] = findSet(father[x]);     //压缩式的查找,在查找过程中更新每个节点的祖先  
	return father[x];
}

void unionSet(int x, int y) {               //合并子树,把节点数小的树合并到节点数大的树  
	x = findSet(x);
	y = findSet(y);

	if (x == y)
		return;

	if (rnk[x] >= rnk[y]) {
		father[y] = x;
		rnk[x] += rnk[y];
	}
	else {
		father[x] = y;
		rnk[y] += rnk[x];
	}
}

int flag[105];                             //记录点是否为访问过  
vector<int> tree[105];                     //树的表示  
vector<int> query[105];                    //查询的表示  
void tarjan(int u)
{                        //访问到集合u时  
	for (int i = 0; i<tree[u].size(); i++) {
		int v = tree[u][i]; 
	//	cout << v << endl;//假设这个节点是v  
		tarjan(v);
		unionSet(u, v);                     //将子节点和根节点合并,并查集的作用只是代表一个集合,仅仅当做一个集合使用  
		ancestor[findSet(u)] = u;            //合并后的集合的祖先为u,只要标记这个集合的代表元素的祖先为x就行,这个集合  
											 //内的其他元素能够通过findSet来找到代表,再利用代表找到祖先  
	}
	flag[u] = 1;

	for (int i = 0; i<query[u].size(); i++)
	{
		if (flag[query[u][i]])               //如果另外一个节点已经被访问过,则输出另外一个节点所在集合的祖先  
			cout << u << "和" << query[u][i] << "的最近公共祖先为:" << ancestor[findSet(query[u][i])] << endl;  //找到节点query[u][i]所在集合的代表的祖先,
																									   //也就是这个集合的祖先,只是用代表的ancestor值标记一下而已   
	}
}
int main()
{
	initSet();
	tree[1].push_back(2);
	tree[1].push_back(3);
	tree[2].push_back(4);
	tree[3].push_back(5);
	query[4].push_back(5);
	query[5].push_back(4);
	tarjan(1);
	return 0;
}