离线LCA学习

题目1 : 最近公共祖先·二

时间限制: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

离线代码如下:

/*************************************************************************
    > File Name: t.cpp
    > Author: acvcla
    > QQ:
    > Mail: [email protected]
    > Created Time: 2014年10月16日 星期四 23时22分13秒
 ************************************************************************/
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<vector>
#include<cstring>
#include<map>
#include<queue>
#include<stack>
#include<string>
#include<cstdlib>
#include<ctime>
#include<set>
#include<math.h>
using namespace std;
typedef long long LL;
const int maxn = 1e5 + 10;
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define pb push_back
map<string,int>f1;
map<int,string>f2;
std::vector<int>G[maxn];
map<pair<int,int>,int>ans;
std::vector<int>query[maxn];
int ql[maxn],qr[maxn];
int col[maxn],p[maxn];
int findx(int x){
	return p[x]==x?x:p[x]=findx(p[x]);
}
void Union(int u,int v){
	p[findx(v)]=findx(u);
}
void Init(int n){
	for(int i=0;i<=n;i++){
		query[i].clear();
		G[i].clear();
		col[i]=0;
		p[i]=i;
	}
	f1.clear();
	f2.clear();
	ans.clear();
}
void lca(int u){
	for(int i=0;i<G[u].size();i++){
		lca(G[u][i]);
		Union(u,G[u][i]);
	}
	col[u]=1;
	for(int i=0;i<query[u].size();++i){
		int v=query[u][i];
		if(col[v]){
			ans[make_pair(u,v)]=findx(v);
		}
	}
}
int main(){
		ios_base::sync_with_stdio(false);
		cin.tie(0);
		int n,m;
		while(cin>>n){
			string fa,son;
			int tot=0;
			Init(n+3);
			for(int i=1;i<=n;i++){
				cin>>fa>>son;
				if(!f1[fa]){
					f1[fa]=++tot;
					f2[tot]=fa;
				}if(!f1[son]){
					f1[son]=++tot;
					f2[tot]=son;
				}
				//cout<<f1[fa]<<' '<<f1[son]<<endl;
				G[f1[fa]].pb(f1[son]);
			}
			cin>>m;
			for(int i=0;i<m;i++){
				cin>>fa>>son;
				ql[i]=f1[fa];
				qr[i]=f1[son];
				query[ql[i]].pb(qr[i]);
				query[qr[i]].pb(ql[i]);
			}
			lca(1);
			for(int i=0;i<m;i++){
				int x=0;
				x=ans[make_pair(ql[i],qr[i])];
				if(!x)x=ans[make_pair(qr[i],ql[i])];
				cout<<f2[x]<<endl;
			}
		}
		return 0;
}
时间: 2024-10-16 02:46:17

离线LCA学习的相关文章

POJ 3728 离线 LCA

题意很简单 给一个树(n < 5w) 每个点有个权值,代表商品价格 若干个询问(5w) 对每个询问,问的是从u点走到v点(简单路径),商人在这个路径中的某点买入商品,然后在某点再卖出商品,   最大可能是多少 注意一条路径上只能买卖一次,先买才能卖 用的方法是离线LCA,在上面加了一些东西 对于一个询问, 假设u,v的LCA是f 那么有三种可能, 一个是从u到f 买卖了. 一个是从f到v买卖了,  一个是从u到f之间买了,从v到f卖了 从u到f 我们称为up,  从f到v我们称为down,而从u

HDU 5452(离线LCA,树形dp

题目:给出一个图和它的一个生成树,要求删除生成树上的一条边和若干其他边,使得图不连通.求能删除的最小边数. 思路:考虑dp,对于树上的一条边,删除之后,还需要删除的边的数目就是从这个节点的子树连向其他子树或祖先节点的边数.那么对于一棵子树来说这个统计数目就等于其子树的数目之和减去它的子树之间的边数.减的这一部分可以在tarjan离线LCA算法中实现. /* * @author: Cwind */ //#pragma comment(linker, "/STACK:102400000,102400

HDU 2586 How far away ? 离线lca模板题

How far away ? Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 8712    Accepted Submission(s): 3047 Problem Description There are n houses in the village and some bidirectional roads connecting

HDU 5044 离线LCA算法

昨天写了HDU 3966 ,本来这道题是很好解得,结果我想用离线LCA 耍一把,结果发现离线LCA 没理解透,错了好多遍,终得AC ,这题比起 HDU 3966要简单,因为他不用动态查询.但是我还是错了好多遍  T^T... http://acm.split.hdu.edu.cn/showproblem.php?pid=5044 不多说了  思想不是很清楚的可以看一看我的上一篇博文 HDU 3966 直接贴代码 1 #include<iostream> 2 #include<stdio.

hdoj 2586 How far away ? 【Tarjan离线LCA】

题目:hdoj 2586 How far away ? 题意:给出一个有权树,求任意两点的之间的距离. 分析:思想就是以一个点 root 作为跟变成有根数,然后深搜处理处所有点到跟的距离.求要求的两个点的LCA(最近公共祖先), 然后ans = dis[x] + dis[y] - 2 * dis[LCA(x,y)],可以画图分析一下就知道. 求LCA我用的是Tarjan离线lca,由于询问次数很多,所以这个比较快. AC代码: #include <iostream> #include <

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

传送门: #1067 : 最近公共祖先·二 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 上上回说到,小Hi和小Ho用非常拙劣——或者说粗糙的手段山寨出了一个神奇的网站,这个网站可以计算出某两个人的所有共同祖先中辈分最低的一个是谁.远在美国的他们利用了一些奇妙的技术获得了国内许多人的相关信息,并且搭建了一个小小的网站来应付来自四面八方的请求. 但正如我们所能想象到的……这样一个简单的算法并不能支撑住非常大的访问量,所以摆在小Hi和小Ho面前的无非两种选择: 其一是

LCA学习笔记

LCA学习笔记 http://dongxicheng.org/structure/lca-rmq/ http://blog.csdn.net/wendavidoi/article/details/50670052 dfs序 + st 树上倍增 Tarjan算法 题目 TBD 模板 TBD 原文地址:https://www.cnblogs.com/wuyuanyuan/p/8511682.html

HDU 2586 How far away ? (离线LCA Tarjan算法模板)

How far away ? Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 6422    Accepted Submission(s): 2411 Problem Description There are n houses in the village and some bidirectional roads connecting

SPOJ 10628 Count on a tree(Tarjan离线LCA+主席树求树上第K小)

COT - Count on a tree #tree You are given a tree with N nodes.The tree nodes are numbered from 1 to N.Each node has an integer weight. We will ask you to perform the following operation: u v k : ask for the kth minimum weight on the path from node u