【问题标题】:Range Queries on a path in a tree树中路径上的范围查询
【发布时间】:2017-10-24 16:38:48
【问题描述】:

我在比赛中遇到了这个问题(现在已经结束),我想不出一个省时的算法。

给你一个有 N (

Add x y – 将 y 添加到节点 x 。

AddUp x y – 将 y 添加到 x 、 x 的父级、 x 的父级的父级直到 Root。

之后会有 Q 个查询 (

我做了什么:-

首先我尝试了根据操作更新每个节点的朴素算法,但显然这很耗时。

我也想过使用分段树和延迟传播,但想不出合适的方法。

感谢任何帮助,谢谢!

【问题讨论】:

  • 你能给我这个问题的链接吗?

标签: c++ algorithm path tree segment-tree


【解决方案1】:

首先,构建一个图,其中孩子指向他们的父母。 之后,解析所有更新并将 Add 和 AddUp 的总和分别存储在树的每个节点中。 您的节点应具有以下变量:

sum_add : the sum of all the Add of this node
sum_add_up : the sum of all the AddUp of this node
subtree_sum : the sum of the subtree. Initialize with 0 by now.

现在,使用拓扑顺序横穿你的图,也就是说,如果一个节点的所有子节点都已被处理,你只会处理一个节点,这需要 O(N)。现在让我定义流程函数。

process(V) {
    V.sum_add_up = V.sum_add_up + sum(sum_add_up of all V.children)
    V.subtree_sum = V.sum_add + V.sum_add_up + sum(subtree_sum of all V.children)
}

现在您可以回答 O(1) 中的所有查询。节点V的值查询为V.sum_add + V.sum_add_upV的子树查询为V.subtree_sum

【讨论】:

  • 如果您希望它更正式,或者您是否需要我证明它的时间复杂度,请告诉我。
  • 您好 Gabriel,我将通过您的解决方案并尝试理解它。非常感谢您的帮助!!
【解决方案2】:

这是一棵 Fenwick 树,要解决此类问题,您必须对树执行拓扑排序并计算每个节点的子节点数。

      0
    /   \
   1     2
  / \
 3   4

索引:[0 1,2,3,4] 孩子们:[4,2,0,0,0] 使用拓扑你将获得这个向量 0 1 3 4 2 你需要反转它:

fenwick Pos:  [0,1,2,3,4]
vector values:[2,4,3,1,0]
pos: [5,3,0,2,1]

使用fenwick tree可以执行2种查询,更新查询,范围求和查询 当你只需要更新一个索引调用update(pos[index], y),那么你必须减少所有的下一个值,update(pos[index]+1, -y) 当您需要更新所有家长时,请致电update(pos[index], y)update(pos[index] + childrens[index] + 1, -y);

要知道某个位置的值,您需要在 pos[index] 上调用范围求和查询

【讨论】:

    【解决方案3】:

    我认为这个问题只是二叉搜索树的直接应用,对于插入和查询,它的平均情况成本(在 n 次随机操作之后)O(1.39log(n))

    您所要做的就是递归地添加新节点并同时更新值和求和。

    实现也相当简单(对不起 C#),例如 Add()AddUp() 类似 - 每次转到左子树或右子树时增加值):

    public void Add(int key, int value)
    {
        Root = Add(Root, key, value);
    }
    
    private Node Add(Node node, int key, int value)
    {
        if (node == null)
        {
            node = new Node(key, value, value);
        }
    
        if (key < node.Key)
        {
            node.Left = Add(node.Left, key, value);
        }
        else if (key > node.Key)
        {
            node.Right = Add(node.Right, key, value);
        }
        else
        {
            node.Value = value;
        }
    
        node.Sum = Sum(node.Left) + Sum(node.Right) + node.Value;
    
        return node;
    }
    

    对于我机器上的 100000 个数字,这将转换为这些数字:

    Added(up) 100000 values in: 213 ms, 831985 ticks
    Got 100000 values in:       86 ms, 337072 ticks
    

    对于 100 万个数字:

    Added(up) 1000000 values in: 3127 ms, 12211606 ticks
    Got 1000000 values in:       1244 ms, 4857733 ticks
    

    这段时间是否足够有效?你可以试试完整代码here

    【讨论】:

      猜你喜欢
      • 2021-09-20
      • 1970-01-01
      • 1970-01-01
      • 2016-10-10
      • 1970-01-01
      • 1970-01-01
      • 2013-03-13
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多