HihoCoder 1067 最近公共祖先(ST离线算法)
最近公共祖先·二
描述
上上回说到,小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
离线:Tarjan算法;
在线:ST算法;
#include<cstdio> #include<cstdlib> #include<cstring> #include<algorithm> #include<iostream> #include<map> using namespace std; const int maxn=200010; int tot,ver[maxn],dept[maxn],first[maxn]; int cnt,Laxt[maxn],Next[maxn],To[maxn],vis[maxn]; int dp[maxn][20]; int personnum,m;//人数,问题数 map<string,int>q;//q和name相对 map<int,string>name; void add(int u,int v) { Next[++cnt]=Laxt[u]; Laxt[u]=cnt; To[cnt]=v; } void dfs(int u ,int dep) { vis[u]=true; ver[++tot] = u; first[u] = tot; dept[tot] = dep; for(int i=Laxt[u];i;i=Next[i]) if( !vis[To[i]] ) { dfs(To[i],dep+1); ver[++tot] = u; dept[tot] = dep; } } void ST(int n) { for(int i=1;i<=n;i++) dp[i][0] = i; for(int j=1;(1<<j)<=n;j++) { for(int i=1;i+(1<<j)-1<=n;i++) { int a = dp[i][j-1] , b = dp[i+(1<<(j-1))][j-1]; dp[i][j] = dept[a]<dept[b]?a:b; } } } int RMQ(int l,int r) { int k=0; while((1<<(k+1))<=r-l+1) k++; int a=dp[l][k],b=dp[r-(1<<k)+1][k]; return dept[a]<dept[b]?a:b; } int LCA(int u ,int v) { int x = first[u] , y = first[v]; if(x > y) swap(x,y); int res = RMQ(x,y); return ver[res]; } int init() { int n,i; string s1,s2,Fa_all; scanf("%d",&n); for(i=1;i<=n;i++){ cin>>s1>>s2; if(i==1) Fa_all=s1; if(!q[s1]) q[s1]=++personnum; if(!q[s2]) q[s2]=++personnum; name[q[s1]]=s1; name[q[s2]]=s2; add(q[s1],q[s2]); } dfs(q[Fa_all],1); //搜索 ST(tot);//dp } int main() { init(); string s1,s2; scanf("%d",&m);//问题 for(int i=1;i<=m;i++){ cin>>s1>>s2; int u=q[s1]; int v=q[s2]; cout<<name[LCA(u,v)]<<endl; } return 0; }