倍增法求LCA的一般步骤:
1.建树,确定深度和父子关系。
2求出递推数组;即:f[i][j] 表示 i 节点的第 2 ^ j 倍祖先,可知
f[i][j] = f[f[i][j - 1]][j - 1];
3.对于读入的每对查询进行操作:
<1>.调平,即将两个点调整到同一深度,也就是让深度较大的点跳到和另一个点同一深度的祖先。
<2>.j 从零开始 只要 两个点的2 ^ j 祖先不相等,j++。
<3>如果第二步没有执行,则退出(说明两个点的直接父亲节点就是LCA了)否则两个点跳到各自的2^j - 1倍祖先上, 再执行第二步
#include<cstdio>
#include<string>
#include<map>
#include<vector>
#include<iostream>
#include<queue>
using namespace std;
const int maxn = 100100;
vector<int> a[maxn];
int f[maxn][30];
int de[maxn];
void bfs(int k, int dep){
queue<int> q;
q.push(k);
de[k] = dep;
while(!q.empty()){
int t = q.front();
q.pop();
for(int i = 0; i < a[t].size(); i++){
q.push(a[t][i]);
de[a[t][i]] = de[t] + 1;
}
}
}
int adjust(int x, int k){
while(de[x] > k) {
int j;
if(de[x] == k) return x;
for(j = 0; de[f[x][j]] > k; j++);
j--;
if(j < 0) break;
x = f[x][j];
}
while(de[x] > k) x = f[x][0];
return x;
}
int lca(int x, int y){
if(de[x] < de[y]) y = adjust(y, de[x]);
else if(de[x] > de[y]) x = adjust(x, de[y]);
while(x != y){
int j = 0;
for(; f[x][j] != f[y][j]; j++);
j--;
if(j < 0) break;
x = f[x][j];
y = f[y][j];
}
if(x != y) x = f[x][0];
return x;
}
int main(){
ios::sync_with_stdio(0);
int T, n, m;
cin >> T;
while(T--){
map<string, int> mp;
int tot = 0;
cin >> n >> m;
for(int i = 0; i <= n; i++) a[i].clear();
for(int i = 1; i < n; i++){
string s, st;
cin >> s >> st;
int x, y;
if(mp.count(s) == 0) {
mp[s] = ++tot;
x = tot;
}else x = mp[s];
if(mp.count(st) == 0) {
mp[st] = ++tot;
y = tot;
}else y = mp[st];
f[x][0] = y;
a[y].push_back(x);
}
for(int i = 1; i <= n; i++){
if(f[i][0] == 0) bfs(i, 1);
}
for(int j = 1; j <= 25; j++){
for(int i = 1; i <= n; i++){
f[i][j] = f[f[i][j - 1]][j - 1];
}
}
for(int i = 1; i <= m; i++){
string s1, s2;
cin >> s1 >> s2;
int p1 = mp[s1], p2 = mp[s2];
int lc = lca(p1, p2);
if(lc == p2) cout << de[p1] - de[lc] << endl;
else cout << de[p1] - de[lc] + 1 << endl;
}
}
return 0;
}