#192-[LCA]距离查询

本文介绍了一种树形结构中求两点最短距离的问题,FJ希望奶牛通过马拉松锻炼,但面临奶牛的反对。输入包含树的节点数、边数、边长及方向,以及多个查询点对。输出为各查询点对间的最短路径长度。计算公式为:dis[x]+dis[y]-2*dis[lca(x,y)]。" 125712403,8827682,从零搭建代理环境:Whistle与SwitchyOmega配置全攻略,"['代理模式', '网络代理', 'Chrome插件', '网络安全', '配置教程']

Description

FJ希望他的奶牛们多点锻炼身体(比如马拉松什么的)。但奶牛们很不开心,因为这打破了她们一贯的特色社会主义作风(自行体会)。FJ会给出一棵树,请求出两点间最短距离。

Input

第一行:n,m。n代表节点数量,m代表边的数量。

接下来m行,每行有u,v,dis和一个字符(E/S/W/N,分别为东南西北)。u代表这条边的起点,v为终点,dis则是这条边的长度。

第m+2行为一个K,代表接下来有K个查询。

接下来K行,每行一个g,h,代表FJ想知道g到h的最短长度。

Output

树上距离查询通常有多种方法,以下结合参考内容介绍一些常见方法及其实现思路: ### 树的直径相关合并方法 在处理树上距离查询时,可以利用树的直径的性质。一个集合直径的两端点,在被划分为两个集合后一定是两个集合直径的四个端点中的两个(前提是边权非负)。基于这个性质,可以使用线段树或ST表来进行信息合并和查询- **原理**:假设将集合\(S\)分为两个集合后,另外两个集合的直径的两端点分别为\(a,b\)和\(c,d\),那么\(S\)集合的直径的两端点一定是\(a,b,c,d\)中的两个。在进行查询时,若得到\([l1,r1]\)内的直径\((a,b)\),\([l2,r2]\)内的直径\((c,d)\) ,答案就是\(\max(dist(a,c),dist(b,d),dist(b,c),dist(a,d))\) [^1][^2]。 - **实现步骤**: - 构建线段树或ST表来维护区间内的直径信息。 - 在 `pushup` 操作时,按照上述性质合并信息。 - 进行查询时,根据合并后的信息计算最大距离### 代码示例(伪代码) ```python # 假设已经有计算两点距离的函数 dist # 定义线段树节点 class SegmentTreeNode: def __init__(self, left, right, diameter_endpoints): self.left = left self.right = right self.diameter_endpoints = diameter_endpoints # 构建线段树 def build_segment_tree(arr, left, right): if left == right: # 单个节点的直径端点就是自身 return SegmentTreeNode(left, right, (arr[left], arr[left])) mid = (left + right) // 2 left_child = build_segment_tree(arr, left, mid) right_child = build_segment_tree(arr, mid + 1, right) # 合并左右子树的直径信息 a, b = left_child.diameter_endpoints c, d = right_child.diameter_endpoints max_dist = max(dist(a, c), dist(b, d), dist(b, c), dist(a, d)) if max_dist == dist(a, c): diameter = (a, c) elif max_dist == dist(b, d): diameter = (b, d) elif max_dist == dist(b, c): diameter = (b, c) else: diameter = (a, d) return SegmentTreeNode(left, right, diameter) # 查询区间内的直径 def query_segment_tree(node, l, r): if l <= node.left and node.right <= r: return node.diameter_endpoints mid = (node.left + node.right) // 2 if r <= mid: return query_segment_tree(node.left_child, l, r) elif l > mid: return query_segment_tree(node.right_child, l, r) else: a, b = query_segment_tree(node.left_child, l, mid) c, d = query_segment_tree(node.right_child, mid + 1, r) max_dist = max(dist(a, c), dist(b, d), dist(b, c), dist(a, d)) if max_dist == dist(a, c): return (a, c) elif max_dist == dist(b, d): return (b, d) elif max_dist == dist(b, c): return (b, c) else: return (a, d) ``` ### 其他常见方法 - **最近公共祖先(LCA)**:对于树上任意两点\(u\)和\(v\),它们之间的距离可以通过公式\(dist(u, v) = dist(u, root) + dist(v, root) - 2 * dist(lca(u, v), root)\)计算,其中\(lca(u, v)\)是\(u\)和\(v\)的最近公共祖先。计算LCA可以使用多种算法,如Tarjan算法、倍增算法等。 - **深度优先搜索(DFS)**:可以通过DFS遍历树,记录每个节点到根节点的距离,然后根据上述公式计算任意两点间的距离
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值