Lowest Common AncestorProblem code: TALCA |
All submissions for this problem are available.
Read problems statements in Mandarin Chinese.
In a rooted tree, the lowest common ancestor (or LCA for short) of two vertices u and v is defined as the lowest vertex that is ancestor of both that two vertices.
Given a tree of N vertices, you need to answer the question of the form "r u v" which means if the root of the tree is at r then what is LCA of u and v.
Input
The first line contains a single integer N. Each line in the next N - 1 lines contains a pair of integer u andv representing a edge between this two vertices.
The next line contains a single integer Q which is the number of the queries. Each line in the next Q lines contains three integers r, u, v representing a query.
Output
For each query, write out the answer on a single line.
Constraints
20 points:
- 1 ≤ N, Q ≤ 100
40 points:
- 1 ≤ N, Q ≤ 105
- There is less than 10 unique value of r in all queries
40 points:
- 1 ≤ N, Q ≤ 2 × 105
Example
Input: 4 1 2 2 3 1 4 2 1 4 2 2 4 2 Output: 1 2
LCA算法,二分
</pre><pre name="code" class="cpp">/*题意:给定一颗N个点的树,有Q个询问,每次询问以r为根的u与v的LCA。
先以0号点为root dfs一遍,并得出anc数组。
一个重要的发现:u v两点的LCA一定在u->v这条路径上。易知LCA点一定是与root距离最近的点,
所以我们可以二分u点到LCA的距离,最终确定答案。*/
#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std;
#define MAXL 19
#define MAXN 200005
#define MAXQ 200005
int N, Q;
int a, b;
int r[MAXQ];
int u[MAXQ];
int v[MAXQ];
int dep[MAXN];
int dad[MAXN]; //dad[i]表示i号点的父亲编号
vector< int > T[MAXN]; //邻接表
int anc[MAXN][MAXL]; //anc[i][j]表示i号点向上2^j步所到达的点
int sol[MAXQ];
vector< int > q, nq;
void dfs(int n, int d = 0) { //得到父子关系
dep[n] = d;
for (int i = 0; i < T[n].size(); ++i) {
if (T[n][i] == dad[n]) continue;
dad[T[n][i]] = n;
dfs(T[n][i], d + 1);
}
}
void make_root(int r) {//计算anc数组
dad[r] = -1;
dfs(r);
for (int i = 0; i < N; ++i)
anc[i][0] = dad[i];
for (int j = 1; j < MAXL; ++j)
for (int i = 0; i < N; ++i)
anc[i][j] = anc[anc[i][j - 1]][j - 1];
}
int get(int n, int jump) { //快速得到n向上跳跃jump步所到达的点
for (int i = MAXL - 1; i >= 0; --i)
if (((jump >> i) & 1) == 1)
n = anc[n][i];
return n;
}
int lca(int a, int b) { //求a与b的LCA
if (dep[a] < dep[b]) swap(a, b);
a = get(a, dep[a] - dep[b]);
if (a == b) return a;
for (int i = MAXL - 1; i >= 0; --i) {
if (anc[a][i] != anc[b][i]) {
a = anc[a][i];
b = anc[b][i];
}
}
return dad[a];
}
int kth(int a, int b, int k) { //a向上跳k步,若超出lca了,剩下的步数往b方向跳
int L = lca(a, b);
if (dep[a] - dep[L] >= k)
return get(a, k);
k -= dep[a] - dep[L] ;
return get(b, dep[b] - dep[L] - k );
}
int dist(int a, int b) { //计算a与b的距离
int L = lca(a, b);
return dep[a] + dep[b] - 2 * dep[L];
}
using namespace std;
int main(void)
{
scanf("%d", &N);
for (int i = 0; i < N - 1; ++i) {
scanf("%d%d", &a, &b); --a; --b;
T[a].push_back(b);
T[b].push_back(a);
}
make_root(0);
scanf("%d", &Q);
for (int i = 0; i < Q; ++i) {
int R, U, V;
scanf("%d%d%d", &R, &U, &V); --R; --U; --V;
int lo = 0, hi = dist(U, V), mid;
while (lo < hi) { //二分距离
mid = (lo + hi) / 2;
if (dist(kth(U, V, mid), R) > dist(kth(U, V, mid + 1), R))
lo = mid + 1;
else
hi = mid;
}
printf("%d\n", kth(U, V, lo) + 1);
}
return 0;
}