传送门:

#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 ;
}

hihoCoder #1067 : 最近公共祖先·二 [ 离线LCA tarjan ]的更多相关文章

  1. Hihocoder #1067 : 最近公共祖先·二

    时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 上上回说到,小Hi和小Ho用非常拙劣——或者说粗糙的手段山寨出了一个神奇的网站,这个网站可以计算出某两个人的所有共同祖先中 ...

  2. HihoCoder 1067 最近公共祖先(ST离线算法)

    最近公共祖先·二 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 上上回说到,小Hi和小Ho用非常拙劣——或者说粗糙的手段山寨出了一个神奇的网站,这个网站可以计算出某两个 ...

  3. 【HIHOCODER 1067】最近公共祖先·二(LCA)

    描述 上上回说到,小Hi和小Ho用非常拙劣--或者说粗糙的手段山寨出了一个神奇的网站,这个网站可以计算出某两个人的所有共同祖先中辈分最低的一个是谁.远在美国的他们利用了一些奇妙的技术获得了国内许多人的 ...

  4. LCA(最近公共祖先)离线算法Tarjan+并查集

    本文来自:http://www.cnblogs.com/Findxiaoxun/p/3428516.html 写得很好,一看就懂了. 在这里就复制了一份. LCA问题: 给出一棵有根树T,对于任意两个 ...

  5. hihoCoder week15 最近公共祖先·二

    tarjan求lca  就是dfs序中用并查集维护下,当访问到询问的第二个点u的时候  lca就是第一点的find(fa[v]) fa[v] = u; // 当v为u的儿子 且 v已经dfs完毕 #i ...

  6. hihocoder1067 最近公共祖先·二(tarjin算法)(并查集)

    #1067 : 最近公共祖先·二 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 上上回说到,小Hi和小Ho用非常拙劣——或者说粗糙的手段山寨出了一个神奇的网站,这个网站 ...

  7. 图论--最近公共祖先问题(LCA)模板

    最近公共祖先问题(LCA)是求一颗树上的某两点距离他们最近的公共祖先节点,由于树的特性,树上两点之间路径是唯一的,所以对于很多处理关于树的路径问题的时候为了得知树两点的间的路径,LCA是几乎最有效的解 ...

  8. suoi31 最近公共祖先2 (倍增lca)

    根为r时x.y的公共祖先,就是lca(x,r),lca(x,y),lca(r,y)中深度最大的那一个,不要再在倍增的时候判来判去还判不对了... #include<bits/stdc++.h&g ...

  9. 学习LCA( 最近公共祖先·二)

    http://poj.org/problem?id=1986 离线找u,v之间的最小距离(理解推荐) #include<iostream> #include<cstring> ...

随机推荐

  1. 【转】android技术栈

    android技术栈-现有使用的进行一个汇总(初稿) 2017年04月24日 16:19:40 阅读数:2004 android技术栈 开发工具 Android studio 开发语言 Java 自动 ...

  2. [BZOJ2761][JLOI2011]不重复数字 暴力

    题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=2761 直接暴力. #include<cstdio> #include<c ...

  3. java8的lambda表达式,将List<DTO> 转为 List<DO>

    将List<PhoneDTO>转为List<PhoneDO>,通过java8的lambda表达式来操作,比传统的for循环精简很多: /** * List<PhoneDT ...

  4. Android学习笔记(十九) OkHttp

    一.概述 根据我的理解,OkHttp是为了方便访问网络或者获取服务器的资源,而封装出来的一个工具包.通常的使用步骤是:首先初始化一个OkHttpClient对象,然后使用builder模式构造一个Re ...

  5. Linux常用终端快捷键

    UNIX程序员对键盘以及快捷键的设置都遵循一个标准:"手移动最少的距离,作更多的操作." 所有的类UNIX的终端上都有一些快捷键Ctrl+n = 下,Ctrl+b = 左,Ctrl ...

  6. 用户授权policy

    定义策略类 php artisan make:policy PostPolicy app/Policies/PostPolicy.php public function update(User $us ...

  7. JavaScript 声明全局变量与局部变量

    一.JavaScript 声明全局变量的三种方式: 声明方式一: 使用var(关键字)+变量名(标识符)的方式在function外部声明,即为全局变量,否则在function声明的是局部变量.该方式即 ...

  8. 自制Jquery下拉框插件

    (function ($) { $.fn.select3 = function (option) { $(this).each(function () { var _this = $(this); v ...

  9. uva1228 Integer Transmission

    这道题思维很灵活.也有点套路的意思. 首先规定0,1分别按照原来的顺序接收,只是01换位.这样简化了思维.(否则并不会有更优结果它.,比较好想)最大值和最小值可以贪心得到.那么接下来就是给定一个整数P ...

  10. cocos creator 小记

    一个游戏场景有若干个节点组成,这些包括渲染节点,UI节点.   这里弱化了Layer层的概念. 一个游戏由若干个场景组件. 每个节点由若干个组件和若干子节点组成. 例如UI节点中的 按钮节点.子节点有 ...