![]() |
VOOZH | about |
Binary Lifting is a Dynamic Programming approach for trees where we precompute some ancestors of every node. It is used to answer a large number of queries where in each query we need to find an arbitrary ancestor of any node in a tree in logarithmic time.
In preprocessing, we initialize the ancestor[][] table, such that ancestor[i][j] stores the 2^jth ancestor of node i.
ancestor[i][j] = ancestor[ancestor[i][j-1]][j-1], when ancestor[i][j-1] != -1
The idea is that we can reach (2^j)th ancestor of node i, by making 2 jumps of size (2^(j-1)), that is (2^j) = (2^(j-1)) + (2^(j-1)). After the first jump from node i, we will reach ancestor[i][j-1] and after the 2nd jump from node ancestor[i][j-1], we will reach ancestor[ancestor[i][j-1]][j-1]
Let's say we need to find 100th ancestor of node X, but in the ancestor[][] table we only have 1st, 2nd, 4th, 8th, 16th, 32nd, 64th, 128th .... ancestors of the current node. Now, we will jump to the maximum possible ancestor such that we don't exceed the 100th ancestor (Safe Jump). So, we will jump to the 64th ancestor of the current node. Now, the problem is reduced to finding the (100-64) = 36th ancestor of this node. We can again jump to the maximum possible ancestor which does not exceed 36, that is 32. We keep on doing this till we reach the required node. In order to find the next safe jump, we can use the binary representation of K. Move from the most significant bit to least significant bit and for every set bit j, we take a jump from this node to the (2^j)th ancestor of the node, which is already stored in ancestor[i][j].
For 100th ancestor of node X,
(100)10 = (1100100)2
👁 Binary-Lifting-in-Competitive-Programming
So, to calculate 100th ancestor of Node X,
Move to 64th ancestor of X = ancestor[X][6] = A1
Then, to calculate the 36th ancestor of A1,
Move to the 32nd ancestor of A1 = ancestor[A1][5] = A2
Then, to calculate the 4th ancestor of A2,
Move to the 4th ancestor of A2 = ancestor[A2][2] = A3
3 4 1 -1
Time Complexity: O(N*log(N) + Q*log(N)), where N is the number of nodes and Q is the number of queries.
Space Complexity: O(N*logN)
Suppose we have a tree with N number of nodes, and we need to answer queries Q, wherein for each query we need to find an arbitrary ancestor of any node, we can solve it using the following techniques:
Binary Lifting is used to answer a large number of queries such that in each query we need to calculate an arbitrary ancestor of any node. We have already covered this in the above implementation.
Binary Lifting is also used to calculate the Lowest Common Ancestor of 2 nodes in a tree. Refer this article to know about calculating LCA using Binary Lifting.
Binary Lifting is also used to extract information about the paths in a tree. This can be done by altering the ancestor[][] table by storing extra information in it. Suppose we are given a weighted tree and Q queries. In each query, we are given 2 nodes (X and Y) and we need to find the maximum of all the edges which lie in the path from X to Y. We can solve this problem by storing additional information in the ancestor[][] table. Earlier, ancestor[i][j] stores only (2^j)th ancestor of node i, now we will also store the maximum of all the edges which lie in the path when we jump from node i to (2^j)th node. Now, to calculate the answer, we find the LCA(X, Y) and then divide the path into 2 parts: Path from node X to LCA(X, Y) and Path from LCA(X, Y) to node Y. Now, we return the maximum of both the paths.
0 6 3 4 4 4 5 6 0 6 6 6 6 6 3 6 0 4 4 4 5 4 6 4 0 2 1 5 4 6 4 2 0 2 5 4 6 4 1 2 0 5 5 6 5 5 5 5 0
Time Complexity: O(N*logN + Q*logN), where N is the number of nodes and Q is the number of queries.
Auxiliary Space: O(N*logN)
Problem | Problem Link |
|---|---|
Practice Now | |
Practice Now | |
Practice Now |