https://loj.ac/problem/10130
题目描述
给出一棵(n)个点的树,有(Q)个询问,每次询问节点(x、y)之间的距离。
思路
这是一棵不带权树,所以我们只要知道了(x、y)的(LCA)就可以求出两点间的距离。求(LCA)的方法有很多,这里主要讲倍增求(LCA),这种方法比较好理解也比较简单,而且可以满足大部分题目的需要(常数最小的为树剖)。我们记录(f[i][j])表示(i)的(2^j)祖先是谁,我们可以一遍(dfs)处理处这个数组。接下来对于每个询问的点,我们考虑先把(x、y)跳到同一高度,这个过程可以倍增实现。接下来再把(x、y)同时往上跳,遇到第一个相同的点即为(LCA)。
代码
#include <bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int nxt[N<<1],head[N],to[N<<1],tot;
void add_edge(int x,int y)
{
nxt[++tot]=head[x];
head[x]=tot;
to[tot]=y;
}
int f[N][22],dep[N];
void dfs(int u,int fa)
{
dep[u]=dep[fa]+1;
for(int i=0;i<=19;i++)
f[u][i+1]=f[f[u][i]][i];
for(int i=head[u];i;i=nxt[i])
{
int v=to[i];
if(v==fa)continue ;
f[v][0]=u;
dfs(v,u);
}
}
int LCA(int x,int y)
{
if(dep[x]<dep[y])swap(x,y);
for(int i=19;i>=0;i--)
{
if(dep[f[x][i]]>=dep[y])x=f[x][i];
if(x==y)return y;
}
for(int i=19;i>=0;i--)
if(f[x][i]!=f[y][i])
{
x=f[x][i];
y=f[y][i];
}
return f[x][0];
}
int read()
{
int res=0,w=1;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){res=(res<<3)+(res<<1)+(ch^48);ch=getchar();}
return res*w;
}
void write(int x)
{
if(x<0){putchar('-');x=-x;}
if(x>9)write(x/10);
putchar(x%10+'0');
}
void writeln(int x)
{
write(x);
putchar('
');
}
int main()
{
int n=read();
for(int i=1;i<n;i++)
{
int x=read(),y=read();
add_edge(x,y);add_edge(y,x);
}
dfs(1,0);
int q=read();
while(q--)
{
int x=read(),y=read();
int lca=LCA(x,y);
writeln(dep[x]+dep[y]-2*dep[lca]);
}
return 0;
}