本题大意是在一个图中,求两个点之间的最短路径。这个图有n个点,n-1条边,也可以看成是一棵树。在树上求两点的最小距离,可以看作是LCA问题的延伸。LCA算法详解:倍增法、DFS+ST
本题中给的是图,我们要将这个图转化成树,首先要规定根节点就是编号为1的结点。然后在此基础上求每个结点的深度和其父节点。而两个点之间的最小距离就是:两个点分别到根节点的距离相加,再减去2倍的LCA到根节点的距离。通过LCA的学习,这一点应该不难理解。所以问题在这时就落到了求LCA上。可以直接套用倍增法的的模板。代码如下,详见注释:
#include<iostream>
#include<vector>
using namespace std;
const int maxn=40005;
int t,n,m,a,b,c;
//保存边信息
struct node
{
int to,val;
node(int a,int b)
{
to=a;
val=b;
}
};
vector<node> e[maxn];
int fa[maxn],dep[maxn],dis[maxn];
//遍历图, 求出各节点到根节点的路径长度和各节点的深度,并将每个点的父节点保存下来
void dfs(int root,int pre)
{
dep[root]=dep[pre]+1;
fa[root]=pre;
for(int i=0;i<e[root].size();i++)
{
int v=e[root][i].to;
//避免回到父节点上
if(v==pre)
continue;
dis[v]=dis[root]+e[root][i].val;
dfs(v,root);
}
}
//ST的初始化
int dp[maxn][20];
void init()
{
for(int j=0;(1<<j)<=n;j++)
for(int i=1;i<=n;i++)
dp[i][j]=-1;
for(int i=1;i<=n;i++)
dp[i][0]=fa[i];
for(int j=1;(1<<j)<=n;j++)
{
for(int i=1;i<=n;i++)
{
if(dp[i][j-1]!=-1)
dp[i][j]=dp[dp[i][j-1]][j-1];
}
}
}
//查询函数
int LCA(int a,int b)
{
if(dep[a]<dep[b])
swap(a,b);
for(int i=19;i>=0;i--)
{
if(dep[a]-(1<<i)>=dep[b])
a=dp[a][i];
}
if(a==b)
return a;
for(int i=19;i>=0;i--)
{
if(dp[a][i]!=dp[b][i])
{
a=dp[a][i];
b=dp[b][i];
}
}
return dp[a][0];
}
int main()
{
cin>>t;
while(t--)
{
cin>>n>>m;
for(int i=1;i<=n;i++)
e[i].clear();
for(int i=1;i<n;i++)
{
scanf("%d%d%d",&a,&b,&c);
e[a].push_back(node(b,c));
e[b].push_back(node(a,c));
}
dis[1]=0;
dep[0]=0;
dfs(1,0);
init();
while(m--)
{
scanf("%d%d",&a,&b);
printf("%d\n",(dis[a]+dis[b]-2*dis[LCA(a,b)]));
}
}
return 0;
}