题目描述
给定n个点的树(1是根),m次询问,每次询问两点的LCA;
输入格式
第一行两个整数,n,m;
接下来n-1行,给定正整数a,b,表示a,b间有边;
接下来m行,给定整数a,b,表示询问a,b;
输出格式
对于每个询问,输出它们的LCA;
样例数据
input
3 2
1 2
2 3
1 2
2 3
output
1
2
数据规模与约定
保证所有数据n=m=100000
#include <bits/stdc++.h>
using namespace std;
const int maxn = 100010;
int n, m;
int to[maxn], fr[maxn], edge[maxn];
int fa[maxn][21];
int dep[maxn];
void dfs(int id, int deep, int f)
{
fa[id][0] = f;
dep[id] = deep;
for (int i = edge[id]; i != -1; i = fr[i])
{
int To = to[i];
//cout<<To <<' '<< deep + 1<<' '<<id<<endl;
dfs(To, deep + 1, id);
}
return;
}
void beizeng()
{
for (int j = 1; j <= 20; j++)
{
for (int i = 0; i <= n; i++)
{
int t = fa[i][j - 1];
if (t == -1)
fa[i][j] = -1;
else
fa[i][j] = fa[t][j - 1];
}
}
}
int main()
{
scanf("%d%d", &n, &m);
memset(edge, -1, sizeof edge);
for (int i = 1; i <= n - 1; i++)
{
int a, b;
scanf("%d%d", &a, &b);
to[i] = b;
fr[i] = edge[a];
edge[a] = i;
}
dfs(1, 0, -1);
for (int i = 1;i <= n; i++)
{
for (int j = 0; j <= 20; j++)
cout<<fa[i][j]<<' ';
cout<<endl;
}
beizeng(); // fa[i][j] 打表 O(n * log[n])
dep[-1] = -1;
for (int i = 1; i <= m; i++)
{
int a, b;
scanf("%d%d", &a, &b);
int u = (dep[a] > dep[b] ? a : b); // u 较深
int v = (dep[a] > dep[b] ? b : a); // v 较浅
if (dep[u] != dep[v])
{
for (int j = 20; j >= 0; j--) // 使 u v 深度相同
{
int t = fa[u][j];
if (dep[t] == dep[v])
{
u = t;
break;
}
else if (dep[t] < dep[v])
continue;
u = t;
}
}
if (u == v)
{
printf("%d\n", u);
continue;
}
for (int j = 20; j >= 0; j--) // 两点一起向上找公共父节点
{
int p = fa[u][j];
int q = fa[v][j];
if (p == q)
continue;
u = p;
v = q;
}
printf("%d\n",fa[u][0]);
}
return 0;
}