📜  门| GATE-CS-2017(套装2)|问题 13(1)

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

门| GATE-CS-2017(套装2)|问题 13

本题要求实现一个函数,具体内容和要求如下:

题目描述

现在有一个含有n个节点的有向图G,每个节点有一个权重值,权重值为正整数。现在有一个整数x,请你编写一个函数 numberOfPaths(G, x),需返回以下两个必要信息的元组:

  1. S_x: 所有起点为x的路径总数;
  2. S_m: 所有中点为x的路径总数。即:对于每条非x起点到非x终点不经过x的路径(从u到v),如果x在这条路径的中间,则该路径计入S_m。请注意,不计入起点终点为x的路径。

由于答案可能相当大,这里请你输出对1000000007取模的结果。

函数格式
def numberOfPaths(G: List[List[int]], x: int) -> Tuple[int, int]:
参数说明

参数 | 说明 ----|---- G | 给定的有向图,G[i]表示从节点i出发可以到达的节点集合,节点权重值已经存储在了一个元组weightList中,节点的序号从1开始编号。 x | 给定的整数x,表示需要统计的中点或起点

返回值说明

说明 | 类型 -----|----- S_x | int S_m | int

代码示例

下面给出参考代码示例,其中weightList为节点权重列表。

from typing import List, Tuple

def numberOfPaths(G: List[List[int]], x: int) -> Tuple[int, int]:
    mod = 1000000007
    n = len(G)
    weightList = [0] * (n+1) #节点权重列表

    Sroot = [0] * (n+1)
    Sx = [0] * (n+1)
    Stot = 0

    totChild  = [1]*(n+1)
    f = [0]*(n+1)
    g = [0]*(n+1)
    vis = [False]*(n+1)

    # 获取节点权重列表
    for i in range(1, n+1):
        weightList[i] = input()

    def dfs(u):
        Sx[u] = Sroot[u]
        for v in G[u]:
            if v == f[u] or vis[v]:
                continue
            f[v] = u
            dfs(v)
            totChild[u] += totChild[v]
            Sx[u] = (Sx[u] + g[u]*f[v]%mod + f[u]*g[v]%mod + f[u]*f[v]%mod*totChild[v]%mod)%mod
            g[u] = (g[u] + g[v] + f[v]*totChild[v]%mod)%mod
        vis[u] = True

    def dfs2(u):
        nonlocal Stot
        Sroot[u] = 1
        Stot = (Stot + weightList[u])%mod
        for v in G[u]:
            if vis[v]:
                continue
            dfs2(v)
            Sroot[u] = (Sroot[u] + Sroot[v])%mod

    dfs2(x)
    vis[x] = True
    for v in G[x]:
        f[v] = x
        dfs(v)
        totChild[x] += totChild[v]
        S_x = (S_x + g[x]*f[v]%mod + f[x]*g[v]%mod + f[x]*f[v]%mod*totChild[v]%mod)%mod

    S_m = (Stot*S_x*S_x%mod-2*Sx[x]*S_x%mod*S_x%mod)%mod
    return (S_x, S_m)
代码说明

首先,定义节点总数n,以及一个长度为n+1的Sroot、Sx、weightList数组,还有对应长度的totChild、f、g、vis数组。

然后,定义dfs函数,其实现可以考虑右图中的递归过程。

def dfs(u):
        Sx[u] = Sroot[u]
        for v in G[u]:
            if v == f[u] or vis[v]:
                continue
            f[v] = u
            dfs(v)
            totChild[u] += totChild[v]
            Sx[u] = (Sx[u] + g[u]*f[v]%mod + f[u]*g[v]%mod + f[u]*f[v]%mod*totChild[v]%mod)%mod
            g[u] = (g[u] + g[v] + f[v]*totChild[v]%mod)%mod
        vis[u] = True

其中,Sx[u]表示抵达u节点经过x这个中转点的路径数量,Sroot[u]表示u节点的子孙节点到u节点的最短路径数量。

在dfs过程中,对于从u出发可以到达的每个节点v,统计从v到u节点的路径数量:

  1. 对于每个子树v,更新totChild[u];
  2. 更新中间点为v的路径数量Sx[u];
  3. 根据totChild[u]计算g[u]。

在每个节点遍历完成后,标记其已遍历过,更新Sx、Sroot数组。

例如,对于Sx[u],可以通过下列方式得到:

  • Sx[u] = Sroot[u],代表u起点构成的Sx集合中,包含起点u的路径数量为Sroot[u];
  • 对于从u节点可以到达的其它节点v,不考虑v为u的父节点的情况下,挑出v节点所构成的子树中的某个节点j,则v指向j的边恰好经过了x,此时该路径就满足条件(路径不包含起点x和终点x,中转点x恰好为其所经过路径的中点);
  • 对于上述路径,其长度为路径从j到u的长度rt加上1(表示从v到j所经过的边长为1)加上路径从v指向j的长度(即通过边e保证j是v的一个子树节点),即Sx[j]+rt+1+len(e),其中rt=totChild[v]-totChild[j]。

同时,编号为x的节点自身也单独处理,从而在dfs函数的操作中避免了x被多次计数的情况。

最后,计算S_x、S_m。

复杂度分析

上述算法中dfs和dfs2的时间复杂度都为O(N),根据题目中N≤20000,可以考虑直接使用该算法。

因此,该算法的时间复杂度为O(N),空间复杂度为O(N)。