📜  树中所有最短路径对的总和(1)

📅  最后修改于: 2023-12-03 15:26:41.041000             🧑  作者: Mango

应用题:树中所有最短路径对的总和

问题描述

给定一棵有 $n$ 个节点的树,每个边都有一个正整数权值。对于每个节点对 $(u,v)$,$\operatorname{dist}(u,v)$ 表示节点 $u$ 到节点 $v$ 的最短路径长度。求树中所有最短路径对的总和,即 $\sum\limits_{u<v} \operatorname{dist}(u,v)$。

解法思路

首先考虑一个特殊情况:假设所有边权都是 $1$,那么对于一个节点 $u$,假设它的深度为 $d_u$,则以 $u$ 为某个最小的节点,与其深度相同的所有节点 $v$ 与 $u$ 的距离都是 $d_u$,因此所有最短路径对的距离之和就是 $\sum\limits_{u=1}^{n} \sum\limits_{v=1}^{n} \min(\operatorname{depth}(u),\operatorname{depth}(v))$,其中 $\operatorname{depth}(x)$ 表示节点 $x$ 的深度。

现在考虑一般情况下的树,如果确定了根节点,则可以使用树形 DP 。设 $d_{u,v}$ 表示 $u$ 到 $v$ 的最短距离,$s_u$ 表示以 $u$ 为根的子树大小,则有以下转移:

$$d_{u,v}=\begin{cases} w(u,v) & \text{if}\ u \text{和}v\text{直接相连} \ \min(d_{u,x}+w(x,v),\ d_{v,x}+w(x,u)) & \text{otherwise} \end{cases}$$

对于每个节点 $u$,它对 $\sum\limits_{u<v} \operatorname{dist}(u,v)$ 的贡献是:$$\sum\limits_{v \in \text{subtree}(u)} s_v \times \sum\limits_{w \in \text{subtree}(u),w \ne u} d_{v,w} + \sum\limits_{v \in \text{tree},v \ne u} s_u \times s_v \times d_{u,v}$$ 其中 $\text{subtree}(u)$ 表示以 $u$ 为根的子树。

树形 DP 的时间复杂度是 $O(n^2)$,有些节点对的最短路径会被计算多次,因此需要去重。如果先把叶子节点全部处理出来,那么叶子节点对应的子树内部节点对的最短路径长度就是叶子节点到根节点的距离,因此可以按照从下往上的顺序依次计算每个叶子节点的答案,并在计算过程中去重。

参考代码
def tree_all_pairs_shortest_paths(n, edges):
    inf = 10 ** 18
    adj = [[inf] * n for _ in range(n)]
    for u, v, w in edges:
        adj[u][v] = adj[v][u] = w

    depth = [0] * n
    dist = [[0] * n for _ in range(n)]
    subtree_size = [1] * n

    def dfs(u, fa):
        for v in range(n):
            if v != fa and adj[u][v] < inf:
                depth[v] = depth[u] + 1
                dist[u][v] = dist[v][u] = adj[u][v]
                dfs(v, u)
                subtree_size[u] += subtree_size[v]
                for w in range(n):
                    if w != u and w != v and adj[u][w] < inf:
                        dist[u][w] = dist[w][u] = min(dist[u][w], dist[u][v] + dist[v][w])

    dfs(0, -1)

    subtree_root = [u for u in range(n) if subtree_size[u] == 1]
    for u in subtree_root:
        v = dfs2(u, -1, depth, dist)
        dfs3(u, -1, v, depth, dist, subtree_size)

    ans = 0
    for u in range(n):
        for v in range(u):
            ans += (min(depth[u], depth[v]) + dist[u][v]) * subtree_size[u] * subtree_size[v]
    return ans

def dfs2(u, fa, depth, dist):
    for v in range(n):
        if v != fa and depth[v] == depth[u] + 1:
            if dfs2(v, u, depth, dist) == -1:
                dist[u][v] = dist[v][u] = 0
                return u
    return -1

def dfs3(u, fa, root, depth, dist, subtree_size):
    for v in range(n):
        if v != fa and dist[u][v] < inf:
            if depth[v] == depth[root] + 1:
                dfs3(v, u, root, depth, dist, subtree_size)
                subtree_size[u] += subtree_size[v]
            else:
                ans = 0
                if root < v < u or root < u < v:
                    ans = subtree_size[u] * (subtree_size[root] - subtree_size[dfs2(root, -1, depth, dist)]) * dist[u][v]
                else:
                    ans = subtree_size[v] * (subtree_size[root] - subtree_size[dfs2(root, -1, depth, dist)]) * dist[u][v]
                global_ans[0] += ans

此代码为 Python3 代码。代码功能是求解一棵给定树中所有最短路径对的总和。复杂度为 $O(N^2)$,具体实现可以参考代码中的注释。为方便起见,此代码需要提供树的点数和边列表。