题目链接:
题解思路:
面对10^5个 名字和10^5条询问,肯定要用到特殊的方法:
1.把所有的询问先存下来,然后再遍历一次整棵树得到所有答案
2.遍历的过程中 查询含当前节点的 所有询问,然后找到询问中的另一个节点;查看另一个节点的状态。
如果另一个节点未访问过,接下来处理;
如果另一个节点正在被访问(子节点未访问完),则答案就是这个节点
如果另一个节点已被访问过,则答案是它的正在被访问的根节点
3. 第二点的状态需要并查集处理
未遍历时所有节点标记为未访问
开始遍历,该节点标记为自己的下标
遍历完后和它的父节点合并
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<map>
#include<vector>
#define MAXN 100050
using namespace std;
map<string,int>id; //保存名字的标号
vector<int>edge[MAXN]; //邻接表
vector<int>query[MAXN]; //保存含某个名字的所有问题的标号
string name[MAXN]; //保存名字
string q1[MAXN],q2[MAXN]; //保存每个问题的左右两个名字
int ans[MAXN];
int fa[MAXN];
int name_s=1;
int get_id(string str)
{
if(!id[str])
{
id[str]=name_s;
name[name_s++]=str;
}
return id[str];
}
int Find(int x)
{
return x==fa[x]?x:fa[x]=Find(fa[x]);
}
void dfs(int loc,int pre) //前序遍历
{
fa[loc]=loc;
for(int i=0;i<edge[loc].size();i++)
dfs(edge[loc][i],loc);
for(int i=0;i<query[loc].size();i++) //含这个节点(名字)的所有问题
{
int j=query[loc][i];
int tmp=loc==id[q1[j]]? id[q2[j]] : id[q1[j]]; //找当前问题的另一个名字
if(fa[tmp]==-1) //另一个名字没访问过
continue;
ans[j]=Find(tmp);
}
fa[loc]=Find(fa[pre]); //合并到当前节点的根节点
return;
}
int main()
{
int n;
string a,b;
int t1,t2;
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
cin>>a>>b;
t1=get_id(a);
t2=get_id(b);
edge[t1].push_back(t2);
}
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
cin>>q1[i]>>q2[i];
query[id[ q1[i] ] ].push_back(i);
query[id[ q2[i] ] ].push_back(i);
}
memset(fa,-1,sizeof(fa));
fa[0]=0;
dfs(1,0);
for(int i=1;i<=n;i++)
cout<<name[ans[i]]<<endl;
return 0;
}
hihocoder 1067 最近公共祖先·二 并查集+stl
原文:http://blog.csdn.net/axuan_k/article/details/46042903