题目链接:http://hdu.hustoj.com/showproblem.php?pid=2586
题意:N 个节点,N-1 条边,任意两点之间只有唯一的一条路径。给出 M 个询问,求给出的询问中两点间的距离
思路:求 B、C 两点间的距离,设 A 点为 B、C 两点的最近公共祖先,D 为任意一点,则有 |BC| = |BD| + |CD| - 2*|AD|。所以此题用 LCA - tarjan 算法求出两点的最近公共祖先即可。
有关 Tarjan 算法的介绍
https://blog.youkuaiyun.com/cutedumpling/article/details/83188775
https://www.cnblogs.com/JVxie/p/4854719.html
解题步骤:
1、用数组模拟邻接表(前向星存图)可以解决 Tarjan 算法对于询问产生的结果无序的问题。
2、选取节点 1 为根节点,用 dis 数组来储存任意一点到节点 1 的距离。
3、用 dfs 遍历所有节点,用并查集来合并节点。
4、注意无向图在遍历与当前 u 有询问关系的节点时,将结果赋值给反向边。
AC代码如下:
#include<bits/stdc++.h>
using namespace std;
#define runfile freopen("E:/Code/CB/Root/data.txt", "r", stdin)
#define stopfile fclose(stdin)
const int maxn = 40005;
const int INF = 0x3f3f3f3f;
const int MOD = 998244353;
int n,m,first[maxn],first1[maxn],flag[maxn],vis[maxn],f[maxn],dis[maxn];
struct edge{
int u,v,w,next;
edge(){}
edge(int _u, int _v, int _w, int _next) : u(_u), v(_v), w(_w), next(_next){}
}g[2*maxn], query[2*maxn];
void init()
{
memset(first, -1, sizeof(first));
memset(first1, -1, sizeof(first1));
memset(flag, 0, sizeof(flag));
memset(vis, 0, sizeof(vis));
for(int i = 1; i <= n; i++)
{
f[i] = i;
dis[i] = 0;
}
}
void add_edge(int i, int from, int to, int cost)
{
g[i] = edge(from, to, cost, first[from]);
first[from] = i;
//无向图
g[i+n-1] = edge(to, from, cost, first[to]);
first[to] = i+n-1;
}
void add_edge1(int i, int from, int to)
{
query[i] = edge(from, to, 0, first1[from]);
first1[from] = i;
//无向图
query[i+m] = edge(to, from, 0, first1[to]);
first1[to] = i+m;
}
int Find(int x)
{
while(x != f[x]) x = f[x];
return f[x];
}
void Merge(int x, int y)
{
int fx = Find(x);
int fy = Find(y);
if(fx == fy) return;
f[x] = y;
}
void tarjan(int u)
{
flag[u] = 1;
for(int i = first[u]; i != -1; i = g[i].next)
{
if(!flag[g[i].v])
{
dis[g[i].v] = dis[u] + g[i].w;
tarjan(g[i].v);
vis[g[i].v] = 1;
Merge(g[i].v, u);
}
}
//如果是叶子节点
for(int i = first1[u]; i != -1; i = query[i].next)
{
if(vis[query[i].v])
{
query[i].w = dis[u] + dis[query[i].v] - 2*dis[Find(query[i].v)];
//处理输入中出现反向边的情况,例如 1、2,2、1
//无向图
if(i > m-1)
query[i-m].w = query[i].w;
else
query[i+m].w = query[i].w;
}
}
}
void print()
{
for(int i = 0; i < m; i++)
cout<<query[i].w<<endl;
}
int main()
{
//runfile;
int T;
cin>>T;
while(T--)
{
cin>>n>>m;
init();
int from,to,cost;
for(int i = 0; i < n-1; i++)
{
cin>>from>>to>>cost;
add_edge(i,from, to, cost);
}
for(int i = 0; i < m; i++)
{
cin>>from>>to;
add_edge1(i, from, to);
}
tarjan(1);
print();
}
//stopfile;
return 0;
}