题目描述
给出n个点的一棵树,多次询问两结点之间的最短距离。(边是双向的)
输入
测试数据第一行为2个整数N和M(1<n<=10000,0<m<=20000).N表示点数,M表示询问次数。
下来n-1行,每行3个整数x,y,k,表示点x和点y之间存在一条边长为k(0<k<=100).
再接下来m行,每行2个整数x,y,表示询问点x到点y的最短距离
输出
输出m行。对于每次询问,输出一行询问结果。
样例输入
2 2 1 2 100 1 2 2 1
样例输出
100 100
题解
这题就是在原来的基础上加一个weight数组保存每个点到根节点的距离,输出的不再是深度而是距离。
#include<iostream>
#include<cstring>
using namespace std;
const int maxn=5e5+10;
int num_edge=0;
int first[maxn],father[maxn],dep[maxn],weight[maxn],st[maxn][25];
struct Edge
{
int next;
int to;
int dis;
}edge[maxn*2];
void add_edge(int from,int to,int dis)
{
edge[++num_edge].next=first[from];
edge[num_edge].to=to;
edge[num_edge].dis=dis;
first[from]=num_edge;
}
void dfs(int x,int fa)
{
father[x]=fa;
for(int i=first[x];i!=-1;i=edge[i].next)//遍历与x相连的所有边
{
int to=edge[i].to;
if(to==fa)//防止形成环
continue;
dep[to]=dep[x]+1;
weight[to]=weight[x]+edge[i].dis;
dfs(to,x);
}
}
int get_lca(int x,int y)
{
if(dep[x]<dep[y])
swap(x,y);
for(int i=20;i>=0;i--)
if(dep[st[x][i]]>=dep[y])
x=st[x][i];
if(x==y)
return x;
for(int i=20;i>=0;i--)
if(st[x][i]!=st[y][i])
{
x=st[x][i];
y=st[y][i];
}
return father[x];//return st[x][0]
}
int main()
{
memset(first,-1,sizeof(first));
memset(weight,0,sizeof(weight));
int n,q;
scanf("%d %d",&n,&q);
for(int i=1;i<n;i++)
{
int x,y,z;
scanf("%d %d %d",&x,&y,&z);
add_edge(x,y,z);
add_edge(y,x,z);
}
dep[1]=1;
dfs(1,0);
for(int i=1;i<=n;i++)
st[i][0]=father[i];
for(int j=1;j<=20;j++)
for(int i=1;i<=n;i++)
st[i][j]=st[ st[i][j-1] ][j-1];
while(q--)
{
int x, y, z;
scanf("%d %d", &x, &y);
printf("%d\n",weight[x]+weight[y]-2*weight[get_lca(x,y)]);
}
return 0;
}