题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2586
题意:对于一个有 n 个节点的图,有 n - 1 条无向边,权值给出。有 m 个查询, 每个查询 a b 表示询问 a b 两节点间的距离。
思路:
把这个联通图以树的形式表现出来,取任意两点,假设其最近公共祖先(Least Common Ancestors)为 lca,则两点间的距离等于:
dis(a, b) = dis(a, root) + dis(b, root) - 2 * dis(root, lca); //root为根,每个点到 root 的距离可以在找 LCA 过程中很容易得到。
LCA和代码 (标号对应代码里的注释):
(1)对图的存储,采用链式前向星。
(2)Tarjan lca离线算法,需要知道所有的查询,然后运算。所以需要把询问以及答案存储。
(3)使用到并查集。dis用来存储每个节点到 root 的距离, vis用来标记已访问过的节点(已经递归完成的节点,和当前查询节点的祖先节点),
已经访问过的点可以查询。in用来记录节点的入度, 方便找到 root。
(4)递归形式的并查集查找操作函数(带路径压缩)。
(5)LCA算法函数:首先明确函数的功能,对以 u 为根节点的树,找到所有以树内子节点为 a,以已访问节点为 b的查询的解。
1:设 u 为当前集合的根节点(仅有元素 u ),标记已访问。
2:对 u 的每个子节点递归LCA操作,之后将其加入集合,作为子节点。
3:查询以 u 为 a,以访问节点为 b的解。(注意如果 b 并未被访问过,则无法查询,所以有了(6))
(6)当在查询 a 时,出现 b 并未被访问情况的时候,(a,b)查询就无效了,但是 b 点在后面的过程中,总会被访问,所以当查询 b 时, a 已经访问,
此时有解。且(a,b),(b, a)查询对应结果相同,所以在输入的时候,要作这一步的处理,输出的时候判断有效解并输出即可。
代码:
时间复杂度为 n * q;
1 #include <stdio.h> 2 #include <queue> 3 #include <math.h> 4 #include <string.h> 5 #include <algorithm> 6 using namespace std; 7 8 const int N = 100007; 9 10 struct Edge //(1) 11 { 12 int to; 13 int w; 14 int next; 15 }edge[N]; 16 17 struct Query //(2) 18 { 19 int u; 20 int v; 21 int ans; 22 }query[N]; 23 24 int pre[N], n, q, head[N], vis[N], in[N], dis[N]; //(3) 25 26 int find(int r) //(4) 27 { 28 if (pre[r] == r) 29 return r; 30 else 31 return pre[r] = find(pre[r]); 32 } 33 34 int merge(int u, int v) 35 { 36 int fu = find(u); 37 int fv = find(v); 38 if (fu != fv) 39 pre[fv] = fu; 40 } 41 42 void lca(int u) //(5) 43 { 44 vis[u] = 1; 45 pre[u] = u; 46 for (int r = head[u]; r != -1; r = edge[r].next) 47 { 48 int v = edge[r].to; 49 dis[v] = dis[u] + edge[r].w; 50 lca(v); 51 merge(u, v); 52 } 53 for (int i = 0; i < 2 * q; i++) 54 { 55 int uu = query[i].u; 56 int v = query[i].v; 57 if (uu == u && vis[v]) 58 { 59 int lca = find(v); 60 int res = dis[uu] + dis[v] - 2 * dis[lca]; 61 query[i].ans = res; 62 } 63 } 64 } 65 66 int main() 67 { 68 int t; 69 while (scanf("%d", &t) != EOF) 70 { 71 while (t--) 72 { 73 scanf("%d%d", &n, &q); 74 memset(in, 0, sizeof(in)); 75 memset(vis, 0, sizeof(vis)); 76 memset(head, -1, sizeof(head)); 77 memset(dis, 0, sizeof(dis)); 78 for (int i = 0; i < n - 1; i++) 79 { 80 int u, v, w; 81 scanf("%d%d%d", &u, &v, &w); 82 edge[i].to = v; 83 edge[i].w = w; 84 edge[i].next = head[u]; 85 head[u] = i; 86 in[v]++; 87 } 88 for (int i = 0; i < q; i++) //(6) 89 { 90 scanf("%d%d", &query[2*i].u, &query[2*i].v); 91 query[2*i+1].u = query[2*i].v; 92 query[2*i+1].v = query[2*i].u; 93 } 94 int root; 95 for (int i = 1; i <= n; i++) 96 { 97 if (!in[i]) 98 root = i; 99 } 100 lca(root); 101 for (int i = 0; i < q; i++) 102 { 103 int maxx = max(query[2*i].ans, query[2*i+1].ans); 104 printf("%d\n", maxx); 105 } 106 } 107 } 108 return 0; 109 }