题目描述:
读入一棵以1为根的树,q次询问,每次给定x和y,问x和y的最近公共祖先是哪一个节点。
树的读入格式:n-1行每行两个整数x、y,表示一条连接x和y的边,保证输入的图形成一棵树。
输入格式:
第一行两个整数为n和q,之后按题目描述读入一棵树。
再之后q行,每行两个整数x和y,表示一组询问。
输出格式:
q行,每行1个整数表示答案。
样例输入1:
10 5
2 1
2 3
2 4
5 4
3 6
7 6
8 1
1 9
1 10
9 8
3 1
7 4
6 2
10 7
样例输出1:
1
1
2
2
1
\(1\le n,q\le 10^5\)
设 \(fa_{x,i}\) 表示 \(x\) 的 \(2^i\) 级祖先,由二进制可知正确性(循环时i从大到小,详见代码),可以 \(O(n\log n)\)预处理,\(O(\log n)\)查询。
#include<bits/stdc++.h>
using namespace std;
const int N=100005;
vector<int>g[N];
int fa[N][21],n,q,dep[N];
void dfs(int u,int deep,int fat) {
fa[u][0]=fat;
dep[u]=deep;
for(int i=0; i<g[u].size(); ++i) {
int v=g[u][i];
if(v==fat)continue;
dfs(v,deep+1,u);
}
}
int lca(int x,int y)
{
if(dep[x]<dep[y])swap(x,y);
for(int i=20;i>=0;--i)
if(dep[fa[x][i]]>=dep[y])x=fa[x][i];
if(x==y)return x;
for(int i=20;i>=0;--i)
{
if(fa[x][i]!=fa[y][i])
{
x=fa[x][i];
y=fa[y][i];
}
}
return fa[x][0];
}
int main() {
scanf("%d%d",&n,&q);
for(int i=1,x,y;i<n;++i)
{
scanf("%d%d",&x,&y);
g[x].push_back(y);
g[y].push_back(x);
}
dfs(1,1,1);
for(int i=1; i<=20; ++i) {
for(int j=1; j<=n; ++j) {
fa[j][i]=fa[fa[j][i-1]][i-1];
}
}
while(q--)
{
int x,y;
scanf("%d%d",&x,&y);
printf("%d\n",lca(x,y));
}
return 0;
}