private void RunHLD(Vertex vertex, bool startsNewChain) { if (startsNewChain) { _hldChainHeads.Add(vertex); } vertex.HLDChainIndex = _hldChainHeads.Count - 1; _hldBaseArray.Add(vertex); vertex.HLDBaseArrayIndex = _hldBaseArray.Count - 1; if (vertex.Children.Any()) { var heaviestChild = vertex.Children[0]; for (int i = 1; i < vertex.Children.Count; ++i) { if (vertex.Children[i].SubtreeSize > heaviestChild.SubtreeSize) { heaviestChild = vertex.Children[i]; } } RunHLD(heaviestChild, startsNewChain: false); for (int i = 0; i < vertex.Children.Count; ++i) { if (vertex.Children[i] != heaviestChild) { RunHLD(vertex.Children[i], startsNewChain: true); } } } }
public Vertex[] GetEulerTour() { // For all n - 1 edges, we take the edge down to its child and then, eventually, back up // to its parent. So each edge contributes 2 vertices to the tour, and we get the root // initially without using any edges, so that's 2*(n - 1) + 1 = 2n - 1 vertices. var eulerTour = new Vertex[2 * VertexCount - 1]; int eulerTourIndex = -1; var verticesToVisit = new Stack <Vertex>(); verticesToVisit.Push(Root); while (verticesToVisit.Count > 0) { var vertex = verticesToVisit.Peek(); eulerTour[++eulerTourIndex] = vertex; // If the EulerTourInitialIndex is null, it's the first time we're visiting the vertex. if (!vertex.EulerTourInitialIndex.HasValue) { vertex.EulerTourInitialIndex = eulerTourIndex; } if (vertex.EulerTourChildCounter == vertex.Children.Count) { verticesToVisit.Pop(); } else { verticesToVisit.Push(vertex.Children[vertex.EulerTourChildCounter++]); } } return(eulerTour); }
// Here's a good guide: https://www.geeksforgeeks.org/find-lca-in-binary-tree-using-rmq/. private Vertex GetLeastCommonAncestor(Vertex firstVertex, Vertex secondVertex) { int firstInitialIndex = firstVertex.EulerTourInitialIndex.Value; int secondInitialIndex = secondVertex.EulerTourInitialIndex.Value; return(firstInitialIndex < secondInitialIndex ? _eulerTourSegmentTree.Query(firstInitialIndex, secondInitialIndex) : _eulerTourSegmentTree.Query(secondInitialIndex, firstInitialIndex)); }
private WeightedRootedTree(int vertexCount, int rootID) { var vertices = new Vertex[vertexCount]; for (int id = 0; id < vertexCount; ++id) { vertices[id] = new Vertex(this, id); } Vertices = vertices; Root = vertices[rootID]; }
// Finds the edge of maximum weight on the path up the tree from the descendant vertex // to the ancestor vertex. The HLD segment tree allows for log(n) querying when vertices // are within the same HLD chain. Some chain hopping may be necessary, but as the links // above mention, there are no more than log(n) chains. private int?QueryUp(Vertex descendantVertex, Vertex ancestorVertex) { int?pathMaximumEdgeWeight = null; while (true) { if (descendantVertex.HLDChainIndex == ancestorVertex.HLDChainIndex) { if (descendantVertex == ancestorVertex) { return(pathMaximumEdgeWeight); // Could still be null if initial vertices were equal. } // Consider the following tree, rooted at V0: V0 --- V1 -- V2 -- V3. // Say we're querying from V3 (descendant) to V1 (ancestor). Along that path we need // to consider V3's edge to V2, and V2's edge to V1. In the HLD segment tree, vertices // correspond with the edge to their parent. So we need to query the range between the // descendant and one before the ancestor, V3 to V2. (The base array for the segment tree // has ancestors appearing before descendants, explaining the start & end indices below.) int chainMaximumEdgeWeight = _hldBaseArraySegmentTree.Query( ancestorVertex.HLDBaseArrayIndex.Value + 1, descendantVertex.HLDBaseArrayIndex.Value).Weight.Value; return(Math.Max(pathMaximumEdgeWeight ?? 0, chainMaximumEdgeWeight)); } else { var descendantChainHead = _tree.HLDChainHeads[descendantVertex.HLDChainIndex.Value]; // Query through the descendant's chain head, which considers all the edges from the // descendant to the chain head, plus the edge from the chain head to the next chain. int descendantChainMaximumEdgeWeight = _hldBaseArraySegmentTree.Query( descendantChainHead.HLDBaseArrayIndex.Value, descendantVertex.HLDBaseArrayIndex.Value).Weight.Value; pathMaximumEdgeWeight = Math.Max(pathMaximumEdgeWeight ?? 0, descendantChainMaximumEdgeWeight); // Advance to the bottom of the next chain. descendantVertex = descendantChainHead.Parent; } } }