斐波那契在树上求和

时间:2017-07-22 22:35:50

标签: algorithm tree fibonacci lowest-common-ancestor

鉴于具有n个节点的树(n可以与2 * 10^5一样大),每个节点都有与之关联的成本,让我们定义以下函数:

  • g(u, v) = the sum of all costs on the simple path from u to v
  • f(n) = the (n + 1)th Fibonacci number (n + 1 is not a typo)

我正在处理的问题要求我计算树模f(g(u, v))中所有可能的节点对的10^9 + 7之和。

举个例子,我们来看一个带有3个节点的树。

  • 不失一般性,假设节点1是根,其子节点为23
  • costs[1] = 2, cost[2] = 1, cost[3] = 1

g(1, 1) = 2; f(2) = 2

g(2, 2) = 1; f(1) = 1

g(3, 3) = 1; f(1) = 1

g(1, 2) = 3; f(3) = 3

g(2, 1) = 3; f(3) = 3

g(1, 3) = 3; f(3) = 3

g(3, 1) = 3; f(3) = 3

g(2, 3) = 4; f(4) = 5

g(3, 2) = 4; f(4) = 5

对所有值求和,并以10^9 + 7模结果得出26作为正确答案。

我的尝试:

我实现了一种算法,通过使用稀疏表找到最低共同祖先来计算g(u, v)中的O(log n)

为了找到合适的Fibonacci值,我尝试了两种方法,即在矩阵形式上使用取幂,另一种方法是注意序列modulo 10^9 + 7是循环的。

现在是非常棘手的部分。无论我如何进行上述计算,在计算所有可能O(n^2)的总和时,我仍然最终会达到f(g(u, v))对。我的意思是,只有n * (n - 1) / 2对才有明显的改善,但这仍然是二次的。

我错过了什么?我已经在这里工作了几个小时,但是我没有办法在不实际生成二次算法的情况下得到这笔钱。

2 个答案:

答案 0 :(得分:2)

要知道节点X的成本要包含在总和中的次数,我们将其他节点划分为3个(或更多)组:

  • 连接到X
  • 左侧的子树A.
  • 连接到X
  • 右侧的子树B.
  • (子树C,D ......如果树不是二进制)
  • 通过X的父
  • 连接的所有其他节点Y.

当两个节点属于不同的组时,它们的简单路径通过X.因此,通过X的简单路径的数量为:

  

#Y + #A×(N - #A)+ #B×(N - #B)

因此,通过计算节点的总数N和X下的子树的大小,您可以计算节点X的成本应包括在总和中的次数。为每个节点执行此操作,您将获得总成本。

这个代码可能很简单。我假设节点N的总数是已知的,并且您可以向节点添加属性(这两个假设都简化了算法,但可以在没有它们的情况下完成)。

我们将添加 child_count 来存储节点的后代数量,并添加 path_count 来存储节点所属的简单路径的数量;两者都初始化为零。

对于每个节点,从根目录开始:

  • 如果不是所有的孩子都被访问过,请去一个未经访问过的孩子。
  • 如果所有孩子都被访问过(或节点是叶子):
    • 增加 child_count
    • 使用N - child_count 增加 path_count
    • 将此节点的 path_count ×费用添加到总费用中。
    • 如果当前节点是根节点,我们就完成了;除此以外:
      • 使用此节点的 child_count 增加父节点的 child_count
      • 使用此节点的 child_count ×(N - child_count )增加父节点的 path_count
      • 转到父节点。

答案 1 :(得分:-1)

以下算法的运行时间为O(n ^ 3)。

树是一个没有循环的强连通图。因此,当我们想要获得所有可能的配对时,成本,我们试图找到所有对的最短路径。因此,我们可以使用Dijkstra的想法和动态编程方法解决这个问题(我从Weiss的书中得到了它)。然后我们将斐波那契函数应用于成本,假设我们已经有一个表来查找。

  • Dijkstra的想法:我们从根开始,搜索从根到所有其他节点的所有简单路径,然后对图上的其他顶点执行此操作。
  • 动态编程方法:我们使用2D矩阵D [] []来表示节点i和节点j之间的最低路径/成本(它们可以交换使用。)。最初,D [i] [i]已经设定。如果节点i和节点j是父/子,则D [i] [j] = g(i,j),这是它们之间的成本。如果节点k在节点i和节点j的成本较低的路径上,我们可以更新D [i] [j],即D [i] [j] = D [i] [k] + D [ k] [j]如果D [i] [j]< D [i] [k] + D [k] [j]否则D [i] [j]。

完成后,我们检查D [] []矩阵并将Fibonacci函数应用于每个单元格并将它们相加,并应用模运算。