范围查询树中的路径

时间:2017-10-24 16:38:48

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

我在竞赛中遇到了这个问题(现在已经结束了),我无法想到一个节省时间的算法。

您将获得一个带根的N(<10 ^ 5)个节点。最初所有节点都具有值0.对树的M更新(&lt; = 10 ^ 5)具有

形式

添加x y - 将y添加到节点x。

AddUp x y - 将y添加到x,x的父项,x uptill Root的父项的父项。

之后将会有Q查询(&lt; = 10 ^ 5)查询,您将被要求提供节点的值或以该节点为根的子树的总和。

我做了什么: -

首先,我尝试了根据操作更新每个节点的天真算法,但显然需要时间。

我还想过使用分段树和懒惰传播,但想不出正确的方法。

感谢任何帮助,谢谢!

3 个答案:

答案 0 :(得分: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_up,而V的子树的查询仅为V.subtree_sum

答案 1 :(得分:1)

这是一棵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树,您可以执行2种查询,更新查询,范围和查询 当您只需要更新索引调用update(pos[index], y)时,则必须减小所有下一个值update(pos[index]+1, -y) 当您需要更新所有父母时,请致电update(pos[index], y)update(pos[index] + childrens[index] + 1, -y);

要了解头寸的价值,您需要在pos [index]上调用范围总和查询

答案 2 :(得分:0)

我认为这个问题只是二元搜索树的直接应用,对于插入和查询,它具有平均情况成本(在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