题目大意:有n个点,同n-1条带有权值的双向边相连,有m个询问,每个询问包含两个数x,y,求x与y的最短距离。
例:
Sample Input
2
3 2
1 2 10
3 1 15
1 2
2 3
2 2
1 2 100
1 2
2 1
Sample Output
10
25
100
100
解题思路:LCA的模板题,刚开始学。。。
用LCA求最短路时必须保证两个顶点之间只有一条边相连,否则就是图而不是树了。
因为n个节点,含有n-1条边,我们可以把它看成一颗树,然后我们把1号节点看成这颗树的根节点,这样我们计算任意两个点x,y的最短距离就可以简单表示为dis[x]+dis[y]-2*dis[lca(x,y)](其中dis[i]表示节点i到根节点的距离,lca(x,y)表示x,y的最近公共祖先)。
如果不会Tarjan离线算法可以看这个博客,http://www.cnblogs.com/JVxie/p/4854719.html,非常简单易懂。
AC代码:
#include<iostream>
#include<vector>
#define maxn 40005
using namespace std;
struct node{
int to,w;
};
int n,m;
vector<node> edge[maxn],que[maxn];
int vis[maxn],f[maxn],dis[maxn],ans[maxn];
void init()
{
for(int i=1;i<=n;i++)
{
f[i]=i;
vis[i]=0;
dis[i]=0;
edge[i].clear();
que[i].clear();
}
}
int find(int x)
{
if(x==f[x]) return x;
else return f[x]=find(f[x]);
}
void merge(int a,int b)
{
int t1=find(a);
int t2=find(b);
if(t1!=t2)
f[t1]=t2;
}
void tarjan(int x)
{
int i;
vis[x]=1;
for(i=0;i<edge[x].size();i++)
{
int v=edge[x][i].to;
if(!vis[v])
{
dis[v]=dis[x]+edge[x][i].w;
tarjan(v);
merge(v,x);
vis[v]=1;
}
}
for(i=0;i<que[x].size();i++)
{
int v=que[x][i].to;
if(vis[v])
ans[que[x][i].w]=dis[v]+dis[x]-2*dis[find(v)];
}
}
int main()
{
int t,i;
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&n,&m);
init();
for(i=1;i<n;i++)
{
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
edge[u].push_back({v,w});
edge[v].push_back({u,w});
}
for(i=1;i<=m;i++)
{
int a,b;
scanf("%d%d",&a,&b);
que[a].push_back({b,i});
que[b].push_back({a,i});
}
tarjan(1);
for(i=1;i<=m;i++)
printf("%d\n",ans[i]);
}
return 0;
}