Heavy Light Decomposition - Anudeep's Blog
Heavy Light Decomposition - Anudeep's Blog
Heavy Light Decomposition - Anudeep's Blog
blog
Now, we know that Balanced Binary Trees and arrays are good for
computation. We can do a lot of operations with O( log N
) complexity on both the data structures.
But wait the tree in the example is special, only 2 nodes had
degree greater than 2. We did a simple decomposition and
achieved better complexity, but in a general tree we need to do
some thing little more complex to get better complexity. And that
little more complex decomposition is called Heavy Light
Decomposition.
Basic Idea
Let us assume that the above is done, So what?. Now the path from
any node A to any node B can be broken into two paths: A to LCA(
A, B ) and B to LCA( A, B ). Details about LCA – Click Here or
Here. So at this point we need to only worry about paths of the
following format: Start at some node and go up the
tree because A to LCA( A, B ) and B to LCA( A, B ) are both such
paths.
• We assumed that we can break tree into chains such that we will
have to change at most log N chains to move from any node
up the tree to any other node.
• Any path can be broken into two paths such both paths start at
some node and move up the tree
• We already know that queries in each chain can be answered
with O( log N ) complexity and there are at most log N chains
we need to consider per path. So on the whole we have O(
log^2 N ) complexity solution. Great!!
Terminology
Special Child : Among all child nodes of a node, the one with
maximum sub-tree size is considered as Special child. Each non
leaf node has exactly one Special child.
Special Edge : For each non-leaf node, the edge connecting the
node with its Special child is considered as Special Edge.
What happens if you go to each node, find the special child and
special edge and mark all special edges with green color and other
edges are still black? Well, what happens is HLD. What would the
graph look like then? Colorful yes. Not just colorful. Green edges
actually forms vertex disjoint chains and black edges will be the
connectors between chains. Let us explore one chain, start at root,
move to the special child of root (there is only one special child, so
easy pick), then to its special child and so on until you reach a leaf
node, what you just traveled is a chain which starts at root node.
Let us assume that root node has m child nodes. Note that all m-1
normal child nodes are starting nodes of some other chains.
What happens if you move from a node to a normal child node of it.
This is the most important part. When you move from a node to
any of its normal child, the sub-tree size is at most half the sub-tree
size of current node. Consider a node X whose sub-tree size is
s and has m child nodes. If m=1, then the only child is special child
(So there is no case of moving to normal child). For m>=2, sub-tree
size of any normal child is <=s/2. To prove that, let us talk about
the sub-tree size of special child. What is the least sub-tree size
possible for special child? Answer is ceil( s/m ) (what is ceil? click
here). To prove it, let us assume it is less than ceil( s/m ). As this
child is the one with maximum sub-tree size, all other normal child
nodes will be at most as large as special child, m child nodes with
each less than ceil( s/m ) will not sum up to s, so with this counter-
intuition. We have the following: The mininum sub-tree size
possible for special child is ceil( s/m ). This being said, the
maximum size for normal child is s/2. So when ever you move
from a node to a normal child, the sub-tree size is at
most half the sub-tree size of parent node.
We stated early that to move from root to any node (or viceversa)
we need to change at most log N chains. Here is the proof;
Changing a chain means we are moving for a node to a normal
child, so each time we change chain we are at least halving the
sub-tree size. For a tree with size N, we can halve it at most log N
times (Why? Well, take a number and keep halving, let me know if
it takes more than ceil( log N ) steps).
Implementation
Algorithm
HLD(curNode, Chain):
Add curNode to curChain
If curNode is LeafNode: return //Nothin
sc := child node with maximum sub-tree size //sc is
HLD(sc, Chain) //Extend
for each child node cn of curNode: //For no
if cn != sc: HLD(cn, newChain) //As tol
int chainNo=0,chainHead[N],chainPos[N],chainInd[N],chainSize[N
void hld(int cur) {
if(chainHead[chainNo] == -1) chainHead[chainNo]=cur;
chainInd[cur] = chainNo;
chainPos[cur] = chainSize[chainNo];
chainSize[chainNo]++;
Example
we calculate LCA(u, v). we call query_up function twice once for the
path u to lca and again for the path v to lca. we take the maximum
of those both as the answer.
while(1) {
if(uchain == vchain) {
int cur = query_tree(1, 0, ptr, posInBase[v]+1, po
if( cur > ans ) ans = cur;
break;
}
int cur = query_tree(1, 0, ptr, posInBase[chainHead[uc
if( cur > ans ) ans = cur;
u = chainHead[uchain];
u = parent(u);
}
return ans;
}
Problems
The End!