题目描述
给定一棵 n个点的树,m 个询问,每次询问点 x到点 y两点之间的距离。
输入格式
第一行一个正整数 ,表示这棵树有 n 个节点;
接下来 n - 1 行,每行两个整数 x,y 表示 x,y 之间有一条连边;
然后一个整数 m,表示有 m 个询问;
接下来 m 行每行两个整数 x,y 表示询问 x 到 y 的距离。
输出格式
输出 一 行,每行表示每个询问的答案。
样例
样例输入
6
1 2
1 3
2 4
2 5
3 6
2
2 6
5 6
样例输出
3
4
数据范围与提示
对于全部数据,1<=n<=1e5 1<=x,y<=n。
思路:因为任意两点之间只有一条边,因此是此图是一棵树。那么此问题就转换成了LCA(最近公共祖先),任意两点之间的距离等于 Dep[x] + Dep[y] - 2 * Dep[LCA(x,y)] ;
上模板了:
#include<bits/stdc++.h>
#define N 100010
using namespace std;
int n,tot;
int Dep[N],f[N][22]; //f[i][j] 第i号节点往上走 2^j 层的节点是谁
int Next[N<<1],first[N<<1],go[N<<1];
void add(int u,int v) //邻接表建树
{
Next[++tot]=first[u];
first[u]=tot;
go[tot]=v;
}
void Deal_first(int u,int father) //预处理
{
Dep[u]=Dep[father]+1; //u的父节点是father ,其深度为父节点的深度+1
for(int i=0; i<=19; i++) //因为 2^j = 2^(j-1) * 2^(j-1)
f[u][i+1]=f[f[u][i]][i]; //动态归化找u往上走的所有层
int e=first[u];
while(e)
{
int v=go[e]; //u -> v
if(v==father) //因为是无向图(father -> u )所以直接跳过即可
{
e=Next[e];
continue;
}
f[v][0]=u; // v 的 (2^(0) = 1) 就是 u
Deal_first(v,u); //递归处理
e=Next[e];
}
}
int LCA(int x,int y)
{
if(Dep[x]<Dep[y]) //规定x的深度大
swap(x,y);
for(int i=20; i>=0; i--) //从”树根“往下枚举
{
if(Dep[f[x][i]]>=Dep[y])
x=f[x][i];
if(x==y)return x; //y 是 x 的祖宗
}
for(int i=20; i>=0; i--) //此时x 和 y 拥有相同的深度
{
if(f[x][i]!=f[y][i]) //两个同时往”树根“走
{
x=f[x][i];
y=f[y][i];
}
}
return f[x][0]; //x,y是深度最浅且不同的点,及LCA的子节点,所以向上找一个
}
int dist(int u,int v)
{
return Dep[u]+Dep[v]-2*Dep[LCA(u,v)];
}
int main()
{
tot=0;
scanf("%d",&n);
for(int i=1; i<n; i++)
{
int x,y;
scanf("%d%d",&x,&y);
add(x,y); //无向图
add(y,x);
}
Deal_first(1,0); //预处理
int q;
scanf("%d",&q);
while(q--)
{
int x,y;
scanf("%d%d",&x,&y);
printf("%d\n",dist(x,y));
}
return 0;
}