Algorithm 将树线性化为数组并回答;sum";路径查询
这个问题是由codechef中的问题引起的。在中,他们建议通过在Algorithm 将树线性化为数组并回答;sum";路径查询,algorithm,tree,fenwick-tree,Algorithm,Tree,Fenwick Tree,这个问题是由codechef中的问题引起的。在中,他们建议通过在DFS遍历中记录每个节点的发现和退出时间,将树线性化为一个数组。现在,我们可以通过对该节点的[发现时间,退出时间]段中发生的事件求和,快速回答有关求和子树的查询。(我们使用Fenwick树快速回答这些问题) 然而,要解决这个问题,我们还需要快速回答求和路径查询。也就是说,将沿a,b之间的最短路径发生的事件求和。这怎么可能?他们给出的答案是: 对于每个有趣的事件,他们都会更新以下内容: update(BT2,event_nod
DFS
遍历中记录每个节点的发现和退出时间,将树线性化为一个数组。现在,我们可以通过对该节点的[发现时间,退出时间]
段中发生的事件求和,快速回答有关求和子树的查询。(我们使用Fenwick
树快速回答这些问题)
然而,要解决这个问题,我们还需要快速回答求和路径
查询。也就是说,将沿a,b
之间的最短路径发生的事件求和。这怎么可能?他们给出的答案是:
对于每个有趣的事件,他们都会更新以下内容:
update(BT2,event_node,1);
update(BT2,out[event_node],-1);
和路径(a,b)
现在是:
int l = lca(a,b);
ans = query(BT2,a) + query(BT2,b) - query(BT2,l) - (l==1 ? 0 : query(BT2, parent[0][l]));
其中query
是前缀和。这怎么正确??当您查看前缀sum直到a
时,可能会遇到许多与l
和a
之间的路径无关的节点 为了线性化a求和路径
查询-树节点之间最短路径上发生的事件之和a,b
我们确实必须执行以下操作:
当在节点v
中发生事件时,我们更新(在[v],1)
和更新(在[v],-1)
IN
是节点的DFS发现时间
和OUT
DFS退出时间
现在查询将是query(在[b]中)-query(在[a]-1中)
。查询(在[b])
是一个前缀和:它从根开始,遍历树,直到到达b
。请注意,对于每个节点v
,我们将不在从根节点到b
的直接路径上传递,我们将发现并最终退出它。仅对于路径上的节点,我们将发现而不是退出。由于我们更新的方式,这意味着我们将有效地对路径根上的节点求和,b
(包括b
)
现在很明显,在查询(在[a]-1中)
中也会发生同样的情况-它是路径根上的节点的总和,a
(这次不包括a
)。减去它们,我们得到a,b
。画一张草图,你们自己会看到的
为了完整性,和子树
的方法在更新
和查询
中有所不同。对于每个事件,您仅更新(在[v]中)
。现在查询sum子树(a)
我们做query(OUT[a])-query(IN[a]-1)
。这次在query(OUT[a])
中,我们将遍历的所有节点相加,直到发现a
,然后将a
子树中的所有节点相加,直到我们退出它。现在我们减去query(在[a]-1中)
-所有节点,直到我们发现a
。我们只剩下a
子树