[英]Range Queries on a path in a tree
I came across this question in a contest (which is now over) and I am not able to think of a time-efficient algorithm.我在一场比赛中遇到了这个问题(现在已经结束),我想不出一个省时的算法。
You are given a rooted Tree of N ( <=10^5) nodes .您将获得 N ( <=10^5) 个节点的有根树。 Initially all nodes have value 0.There would be M updates (<=10^5) to the tree which are of the form
最初所有节点的值都为 0。树会有 M 次更新(<=10^5),其形式如下
Add xy – Add y to node x .添加 xy – 将 y 添加到节点 x 。
AddUp xy – Add y to x , parent of x , parent of parent of x uptill Root. AddUp xy – 将 y 添加到 x , x 的父级, x 的父级的父级直到 Root。
After that there will be Q queries ( <=10^5) queries where you will either be asked to give the value of a node or the sum of subtree rooted at that node.之后将有 Q 个查询 (<=10^5) 个查询,您将被要求提供节点的值或以该节点为根的子树的总和。
What I did:-我做了什么:-
First I tried the naive algorithm that would update each node according to the operation, but obviously it is time taking.首先我尝试了根据操作更新每个节点的朴素算法,但显然这很耗时。
I also thought of using segment trees and Lazy propogation but cannot think of a proper way.我也想过使用段树和延迟传播,但想不出合适的方法。
Any help is appreciated , Thanks!任何帮助表示赞赏,谢谢!
First, construct a graph where the children point to their parents.首先,构建一个图,其中孩子指向他们的父母。 After that, parse all the updates and store in each node of your tree the sum of Add and AddUp separately.
之后,解析所有更新并将 Add 和 AddUp 的总和分别存储在树的每个节点中。 Your node should have the following variables:
您的节点应具有以下变量:
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.
Now, transverse your graph using topological order, ie, you will only process a node if all of its children were already processed, which takes O(N).现在,使用拓扑顺序横向您的图,即,如果它的所有子节点都已被处理,您将只处理一个节点,这需要 O(N)。 Let me now define the process function.
现在让我定义流程函数。
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)
}
Now you can answer all the queries in O(1).现在您可以回答 O(1) 中的所有查询。 The query for the value of a node
V
is V.sum_add + V.sum_add_up
, and the query for the subtree of V
is just V.subtree_sum
.查询节点
V
的值是V.sum_add + V.sum_add_up
,查询V
的子树就是V.subtree_sum
。
This is a Fenwick Tree, for solve this kind of problems you must to execute a topological sort on tree and count the number of childrens for each node.这是一棵 Fenwick 树,要解决此类问题,您必须对树执行拓扑排序并计算每个节点的子节点数。
0
/ \
1 2
/ \
3 4
index: [0 1,2,3,4] childrens: [4,2,0,0,0] With topological you will obtain this vector 0 1 3 4 2 you need to reverse it:索引:[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]
With fenwick tree you can execute 2 kinds of query, update query, range sum query when you need to update a only a index call update(pos[index], y)
, then you must decrease all next values, update(pos[index]+1, -y)
When you need to update all parents call update(pos[index], y)
and update(pos[index] + childrens[index] + 1, -y);
使用 fenwick 树,您可以执行 2 种查询,更新查询,范围总和查询,当您只需要更新一个索引调用
update(pos[index], y)
,然后您必须减少所有下一个值, update(pos[index]+1, -y)
当需要更新所有父级时调用update(pos[index], y)
和update(pos[index] + childrens[index] + 1, -y);
for know value of a position you need to call range sum query on pos[index]要知道位置的值,您需要在 pos[index] 上调用范围总和查询
I think this problem is just a direct application of a Binary Search Tree, which has an average-case cost (after n random operations) O(1.39log(n))
for both inserts and queries.我认为这个问题只是二叉搜索树的直接应用,对于插入和查询,它的平均成本(在 n 次随机操作之后)
O(1.39log(n))
。
All you have to do is recursively add new nodes and update values and sum at the same time.您所要做的就是递归地添加新节点并同时更新值和求和。
Implementation is also fairly simple (sorry for C#), for example for Add()
( AddUp()
is similar - increase value every time you go to left or right subtree):实现也相当简单(对不起 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;
}
For 100000 numbers on my machine this translates to these numbers:对于我机器上的 100000 个数字,这将转换为这些数字:
Added(up) 100000 values in: 213 ms, 831985 ticks
Got 100000 values in: 86 ms, 337072 ticks
And for 1 million numbers:对于 100 万个数字:
Added(up) 1000000 values in: 3127 ms, 12211606 ticks
Got 1000000 values in: 1244 ms, 4857733 ticks
Is this time efficient enough?这个时间够效率吗? You can try complete code here .
您可以在此处尝试完整代码。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.