hihoCoder #1067 : 最近公共祖先·二 [ 离线LCA tarjan ]

传送门:

#1067 : 最近公共祖先·二

时间限制:10000ms
单点时限:1000ms
内存限制:256MB

描述

上上回说到,小Hi和小Ho用非常拙劣——或者说粗糙的手段山寨出了一个神奇的网站,这个网站可以计算出某两个人的所有共同祖先中辈分最低的一个是谁。远在美国的他们利用了一些奇妙的技术获得了国内许多人的相关信息,并且搭建了一个小小的网站来应付来自四面八方的请求。

但正如我们所能想象到的……这样一个简单的算法并不能支撑住非常大的访问量,所以摆在小Hi和小Ho面前的无非两种选择:

其一是购买更为昂贵的服务器,通过提高计算机性能的方式来满足需求——但小Hi和小Ho并没有那么多的钱;其二则是改进他们的算法,通过提高计算机性能的利用率来满足需求——这个主意似乎听起来更加靠谱。

于是为了他们第一个在线产品的顺利运作,小Hi决定对小Ho进行紧急训练——好好的修改一番他们的算法。

而为了更好的向小Ho讲述这个问题,小Hi将这个问题抽象成了这个样子:假设现小Ho现在知道了N对父子关系——父亲和儿子的名字,并且这N对父子关系中涉及的所有人都拥有一个共同的祖先(这个祖先出现在这N对父子关系中),他需要对于小Hi的若干次提问——每次提问为两个人的名字(这两个人的名字在之前的父子关系中出现过),告诉小Hi这两个人的所有共同祖先中辈分最低的一个是谁?

提示一:老老实实分情况讨论就不会出错的啦!

提示二:并查集其实长得很像一棵树你们不觉得么?

输入

每个测试点(输入文件)有且仅有一组测试数据。

每组测试数据的第1行为一个整数N,意义如前文所述。

每组测试数据的第2~N+1行,每行分别描述一对父子关系,其中第i+1行为两个由大小写字母组成的字符串Father_i, Son_i,分别表示父亲的名字和儿子的名字。

每组测试数据的第N+2行为一个整数M,表示小Hi总共询问的次数。

每组测试数据的第N+3~N+M+2行,每行分别描述一个询问,其中第N+i+2行为两个由大小写字母组成的字符串Name1_i, Name2_i,分别表示小Hi询问中的两个名字。

对于100%的数据,满足N<=10^5,M<=10^5, 且数据中所有涉及的人物中不存在两个名字相同的人(即姓名唯一的确定了一个人),所有询问中出现过的名字均在之前所描述的N对父子关系中出现过,第一个出现的名字所确定的人是其他所有人的公共祖先

输出

对于每组测试数据,对于每个小Hi的询问,按照在输入中出现的顺序,各输出一行,表示查询的结果:他们的所有共同祖先中辈分最低的一个人的名字。

样例输入
4
Adam Sam
Sam Joey
Sam Micheal
Adam Kevin
3
Sam Sam
Adam Sam
Micheal Kevin
样例输出
Sam
Adam
Adam

题解:

离线LCA, tarjan

tarjan算法的步骤是(当dfs到节点u时):
1. 在并查集中建立仅有u的集合,设置该集合的祖先为u
2. 对u的每个孩子v:
   2.1 tarjan之
   2.2 合并v到父节点u的集合,确保集合的祖先是u
3. 设置u为已遍历
4. 处理关于u的查询,若查询(u,v)中的v已遍历过,则LCA(u,v)=v所在的集合的祖先

结果:Accepted      提交时间:2015-05-15 16:17:22

1067 最近公共祖先·二 AC G++ 800ms 35MB 1分钟前 查看
 #include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <stack>
#include <cctype>
#include <vector>
#include <cmath>
#include <map> #define ll long long using namespace std; const int N = ;
const int M = ;
const ll mod = ; int n,m;
int tot;
map<string,int> mp;
int f[N];
char s1[N],s2[N];
vector<int> G[N];
int vis[N]; struct PP
{
int next;
int index;
PP(int tn,int ti)
{
next = tn;
index = ti;
}
}; vector<PP> query[N]; string name[N];
string ans[N]; int find(int x)
{
return f[x] == x ? f[x] : f[x] = find(f[x]);
} void merge(int x,int y)
{
int a=find(x);
int b=find(y);
if(a==b) return;
f[b]=a;
} void ini()
{
tot=;
int i;
int x,y;
for(i=;i<=n;i++){
G[i].clear();
query[i].clear();
f[i]=i;
}
for(i=;i<=n;i++){
scanf("%s%s",s1,s2);
if(mp.count(s1)==){
tot++;
mp[s1]=tot;
x=tot;
name[tot]=s1;
}
else{
x=mp[s1];
}
if(mp.count(s2)==){
tot++;
mp[s2]=tot;
y=tot;
name[tot]=s2;
}
else{
y=mp[s2];
} G[x].push_back(y);
G[y].push_back(x);
} scanf("%d",&m);
for(i=;i<=m;i++){
scanf("%s%s",s1,s2);
x=mp[s1];
y=mp[s2];
query[x].push_back(PP(y,i));
query[y].push_back(PP(x,i));
}
} void tarjan(int u,int fa)
{
int i;
int v;
//printf(" u=%d fa=%d\n",u,fa);
for(i=;i<G[u].size();i++){
v=G[u][i];
if(v==fa) continue;
tarjan(v,u);
}
int index;
vis[u]=; for(i=;i<query[u].size();i++){
v=query[u][i].next;
index=query[u][i].index;
// printf(" i=%d v=%d index=%d\n",i,v,index);
if(vis[v]==) continue;
ans[index]=name[find(v)];
}
f[u]=fa;
} void solve()
{
memset(vis,,sizeof(vis));
tarjan(,-);
} void out()
{
int i;
for(i=;i<=m;i++){
cout<<ans[i]<<endl;
}
} int main()
{
//freopen("data.in","r",stdin);
//scanf("%d",&T);
//for(int ccnt=1;ccnt<=T;ccnt++){
while(scanf("%d",&n) != EOF) {
ini();
solve();
out();
}
return ;
}
上一篇:解决Unsupported major.minor version 51.0错误


下一篇:函数矩阵对矩阵求导