问题描述
给定一棵有 NN 个节点的树,每个节点有一个唯一的编号,从 11 到 NN。树的根节点是 11 号节点。接下来,你会得到 QQ 个查询。对于每个查询,你将得到两个节点的编号,你的任务是找到这两个节点的最低公共祖先。
输入格式
第一行包含一个整数 NN,表示树的节点数。
接下来的 N−1N−1 行,每行包含两个整数 UU 和 VV,表示节点 UU 和节点 VV 之间有一条边。
下一行包含一个整数 QQ,表示查询的数量。
接下来的 QQ 行,每行包含两个整数 AA 和 BB,表示你需要找到节点 AA 和节点 BB 的最低公共祖先。
输出格式
对于每个查询,输出一行,该行包含一个整数,表示两个节点的最近公共祖先。
样例输入
5
1 2
1 3
2 4
2 5
3
4 5
3 4
3 5
样例输出
2
1
1
样例说明
对于第一个查询,44 和 55 的最低公共祖先是 22。
对于第二个查询,33 和 44 的最低公共祖先是 11。
对于第三个查询,33 和 55 的最低公共祖先是 11。
测评数据规模
2≤N≤1e5,1≤Q≤1e4,1≤Q≤1e4,1≤U,V,A,B≤N,1≤U,V,A,B≤N,题目保证输入的边形成一棵树。
题解
思路
第一步 利用dfs求出每一个点的祖先,用fa[u][i]来存储,其表示为结点u的第2^i个祖先。
第二步 lca(u, v)查询,利用在dfs中记录的结点深度数组dep,将深度大的一方跳到和另一方深度相同的祖先,此时将两个处于同一深度的结点一起往上跳,直到跳到共同的祖先。
代码如下
#include<bits/stdc++.h>
using namespace std;
using ll = long long;const int N = 5e5 + 9;int n;vector<int>g[N];int dep[N], fa[N][20];void dfs(int u, int father){dep[u] = dep[father] + 1;fa[u][0] = father;for(int i = 1;i <= 19;i ++)fa[u][i] = fa[fa[u][i - 1]][i - 1];for(int v : g[u]){if(v != father)dfs(v, u);}
}int lca(int u, int v){if(dep[u] < dep[v])swap(u, v);// 跳到同一层for(int i = 19;i >= 0;i --)if(dep[fa[u][i]] >= dep[v])u = fa[u][i];// 如果公共祖先就是vif(u == v)return v;// 再跳到公共祖先的下一层for(int i = 19;i >= 0;i --)if(fa[u][i] != fa[v][i])u = fa[u][i], v = fa[v][i];// 最后再往上跳一层return fa[u][0];
}int main(){ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);cin >> n;for(int i = 1;i <= n - 1;i ++){int u,v;cin >> u >> v;g[u].push_back(v);}dfs(1, 0);int q;cin >> q;while(q --){int a, b;cin >> a >> b;cout << lca(a, b) << '\n';}return 0;
}