题意
思路
- 基本思路就是,把节点映射到一个数组的下标中,然后数组记录每个点的深度
- 我们需要保证,每两个节点映射的位置间,存在只存在它俩的最近公共祖先,而不存在其它祖先
- 我们先想一下,如果是一个二叉树,我们只要按照中序周游的顺序,存入数组中,是不是就满足要求了
- 那么,现在这个树不一定是二叉的,那么类似二叉中序遍历,对u节点,访问完第一个子树后,存一下节点u的深度,再访问第二个子树,然后再记一下节点u的深度。。。
- 是不是就和二叉树中序周游记录的方法类似了
- 然后查询的时候我们用两个节点最后一次存入的数组位置去RMQ查询一下最小值即可(其实这里用任意这两个点在数组的位置都可以,用最后一次只是为了方便。。。)
- RMQ这里就用ST算法了~比较方便~
实现
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5+5;
vector<int> g[maxn];
typedef pair<int,int >pii;
#define pb push_back
#define fi first
#define se second
#define mp make_pair
map<string,int> mapp;
string rmapp[maxn];
int id[maxn<<2];
int rid[maxn<<2];
vector<int> a;
int dp[maxn<<2][21];
int n,m,q;
void st(){
for (int i=0;i<a.size()-1;i++){
if (a[i] <= a[i+1]){
dp[i][0] = i;
}
else{
dp[i][0] = i+1;
}
}
for (int j=1;j<=20;j++){
for (int i=0;i+(1<<j)<a.size();i++){
if (a[dp[i][j-1]] <= a[dp[i+(1<<(j-1))][j-1]]){
dp[i][j] = dp[i][j-1];
}
else{
dp[i][j] = dp[i+(1<<(j-1))][j-1];
}
}
}
}
int log2(int x){
int ret = -1;
while(x > 0){
x >>= 1;
ret++;
}
return ret;
}
int ask(int l,int r){
int len = r - l;
len = log2(len);
if(len == -1)
return l;
if (a[dp[l][len]] <= a[dp[r-(1<<len)][len]]){
return dp[l][len];
}
else{
return dp[r-(1<<len)][len];
}
}
void dfs(int u,int dep){
for (int e=0;e<g[u].size();e++){
rid[a.size()] = u;
a.pb(dep);
int v = g[u][e];
dfs(v,dep + 1);
}
rid[a.size()] = u;
id[u] = a.size();
a.pb(dep);
}
int main(){
ios::sync_with_stdio(false);
int now = 1;
cin>>m;
n = m + 1;
string str1,str2;
for (int i=0;i<m;i++){
cin>>str1>>str2;
if (mapp[str1] == 0){
mapp[str1] = now;
rmapp[now] = str1;
now++;
}
if (mapp[str2] == 0){
mapp[str2] = now;
rmapp[now] = str2;
now++;
}
g[mapp[str1]].pb(mapp[str2]);
}
dfs(1,0);
st();
cin>>q;
for (int i=0;i<q;i++){
cin>>str1>>str2;
int tl = mapp[str1];
int tr = mapp[str2];
int l = min(id[tl], id[tr]);
int r = max(id[tl], id[tr]);
cout << rmapp[rid[ask(l, r)]] << endl;
}
return 0;
}