Given an N-ary tree with weighted edge and Q queries where each query contains two nodes of the tree. The task is to find the maximum weighted edge in the simple path between these two nodes.
Examples:
Naive Approach: A simple solution is to traverse the whole tree for each query and find the path between the two nodes.
Efficient Approach: The idea is to use binary lifting to pre-compute the maximum weighted edge from every node to every other node at distance of some
. We will store the maximum weighted edge till
level.
and
where
- j is the node and
- i is the distance of
- dp[i][j] stores the parent of j at
- distance if present, else it will store 0
- mx[i][j] stores the maximum edge from node j to the parent of this node at
- distance.
We’ll do a depth-first search to find all the parents at
distance and their weight and then precompute parents and maximum edges at every
distance.
Below is the implementation of the above approach:
// C++ implementation to find the // maximum weighted edge in the simple // path between two nodes in N-ary Tree #include <bits/stdc++.h> using namespace std;
const int N = 100005;
// Depths of Nodes vector< int > level(N);
const int LG = 20;
// Parent at every 2^i level vector<vector< int > > dp(LG, vector< int >(N));
// Maximum node at every 2^i level vector<vector< int > > mx(LG, vector< int >(N));
// Graph that stores destinations // and its weight vector<vector<pair< int , int > > > v(N);
int n;
// Function to traverse the nodes // using the Depth-First Search Traversal void dfs_lca( int a, int par, int lev)
{ dp[0][a] = par;
level[a] = lev;
for ( auto i : v[a]) {
// Condition to check if its
// equal to its parent then skip
if (i.first == par)
continue ;
mx[0][i.first] = i.second;
// DFS Recursive Call
dfs_lca(i.first, a, lev + 1);
}
} // Function to find the ancestor void find_ancestor()
{ // Loop to set every 2^i distance
for ( int i = 1; i < LG; i++) {
// Loop to calculate for
// each node in the N-ary tree
for ( int j = 1; j <= n; j++) {
dp[i][j]
= dp[i - 1][dp[i - 1][j]];
// Storing maximum edge
mx[i][j]
= max(mx[i - 1][j],
mx[i - 1][dp[i - 1][j]]);
}
}
} int getMax( int a, int b)
{ // Swapping if node a is at more depth
// than node b because we will
// always take at more depth
if (level[b] < level[a])
swap(a, b);
int ans = 0;
// Difference between the depth of
// the two given nodes
int diff = level[b] - level[a];
while (diff > 0) {
int log = log2(diff);
ans = max(ans, mx[ log ][b]);
// Changing Node B to its
// parent at 2 ^ i distance
b = dp[ log ][b];
// Subtracting distance by 2^i
diff -= (1 << log );
}
// Take both a, b to its
// lca and find maximum
while (a != b) {
int i = log2(level[a]);
// Loop to find the 2^ith
// parent that is different
// for both a and b i.e below the lca
while (i > 0
&& dp[i][a] == dp[i][b])
i--;
// Updating ans
ans = max(ans, mx[i][a]);
ans = max(ans, mx[i][b]);
// Changing value to its parent
a = dp[i][a];
b = dp[i][b];
}
return ans;
} // Function to compute the Least // common Ancestor void compute_lca()
{ dfs_lca(1, 0, 0);
find_ancestor();
} // Driver Code int main()
{ // Undirected tree
n = 5;
v[1].push_back(make_pair(2, 2));
v[2].push_back(make_pair(1, 2));
v[1].push_back(make_pair(3, 5));
v[3].push_back(make_pair(1, 5));
v[3].push_back(make_pair(4, 3));
v[4].push_back(make_pair(3, 4));
v[3].push_back(make_pair(5, 1));
v[5].push_back(make_pair(3, 1));
// Computing LCA
compute_lca();
int queries[][2]
= { { 3, 5 },
{ 2, 3 },
{ 2, 4 } };
int q = 3;
for ( int i = 0; i < q; i++) {
int max_edge = getMax(queries[i][0],
queries[i][1]);
cout << max_edge << endl;
}
return 0;
} |
// Java implementation to find the // maximum weighted edge in the simple // path between two nodes in N-ary Tree import java.util.*;
import java.awt.Point;
public class Main
{ static int N = 100005 ;
// Depths of Nodes
static int [] level = new int [N];
static int LG = 20 ;
// Parent at every 2^i level
static int [][] dp = new int [LG][N];
// Maximum node at every 2^i level
static int [][] mx = new int [LG][N];
// Graph that stores destinations
// and its weight
static Vector<Vector<Point>> v = new Vector<Vector<Point>>();
static int n = 0 ;
// Function to traverse the
// nodes using the Depth-First
// Search Traversal
static void dfs_lca( int a, int par, int lev)
{
dp[ 0 ][a] = par;
level[a] = lev;
for ( int i = 0 ; i < v.get(a).size(); i++)
{
// Condition to check
// if its equal to its
// parent then skip
if (v.get(a).get(i).x == par)
continue ;
mx[ 0 ][v.get(a).get(i).x] = v.get(a).get(i).y;
// DFS Recursive Call
dfs_lca(v.get(a).get(i).x, a, lev + 1 );
}
}
// Function to find the ancestor
static void find_ancestor()
{
// Loop to set every 2^i distance
for ( int i = 1 ; i < 16 ; i++)
{
// Loop to calculate for
// each node in the N-ary tree
for ( int j = 1 ; j < n + 1 ; j++)
{
dp[i][j] = dp[i - 1 ][dp[i - 1 ][j]];
// Storing maximum edge
mx[i][j] = Math.max(mx[i - 1 ][j], mx[i - 1 ][dp[i - 1 ][j]]);
}
}
}
static int getMax( int a, int b)
{
// Swapping if node a is at more depth
// than node b because we will
// always take at more depth
if (level[b] < level[a])
{
int temp = a;
a = b;
b = temp;
}
int ans = 0 ;
// Difference between the
// depth of the two given
// nodes
int diff = level[b] - level[a];
while (diff > 0 )
{
int log = ( int )(Math.log(diff) / Math.log( 2 ));
ans = Math.max(ans, mx[log][b]);
// Changing Node B to its
// parent at 2 ^ i distance
b = dp[log][b];
// Subtracting distance by 2^i
diff -= ( 1 << log);
}
// Take both a, b to its
// lca and find maximum
while (a != b)
{
int i = ( int )(Math.log(level[a]) / Math.log( 2 ));
// Loop to find the maximum 2^ith
// parent the is different
// for both a and b
while (i > 0 && dp[i][a] == dp[i][b])
{
i-= 1 ;
}
// Updating ans
ans = Math.max(ans, mx[i][a]);
ans = Math.max(ans, mx[i][b]);
// Changing value to
// its parent
a = dp[i][a];
b = dp[i][b];
}
return ans;
}
// Function to compute the Least
// common Ancestor
static void compute_lca()
{
dfs_lca( 1 , 0 , 0 );
find_ancestor();
}
public static void main(String[] args) {
for ( int i = 0 ; i < LG; i++)
{
for ( int j = 0 ; j < N; j++)
{
dp[i][j] = 0 ;
mx[i][j] = 0 ;
}
}
for ( int i = 0 ; i < N; i++)
{
v.add( new Vector<Point>());
}
// Undirected tree
v.get( 1 ).add( new Point( 2 , 2 ));
v.get( 2 ).add( new Point( 1 , 2 ));
v.get( 1 ).add( new Point( 3 , 5 ));
v.get( 3 ).add( new Point( 1 , 5 ));
v.get( 3 ).add( new Point( 4 , 3 ));
v.get( 4 ).add( new Point( 3 , 4 ));
v.get( 3 ).add( new Point( 5 , 1 ));
v.get( 5 ).add( new Point( 3 , 1 ));
// Computing LCA
compute_lca();
int [][] queries
= { { 3 , 5 },
{ 2 , 3 },
{ 2 , 4 } };
int q = 3 ;
for ( int i = 0 ; i < q; i++) {
int max_edge = getMax(queries[i][ 0 ],
queries[i][ 1 ]);
System.out.println(max_edge);
}
}
} // This code is contributed by decode2207. |
# Python3 implementation to # find the maximum weighted # edge in the simple path # between two nodes in N-ary Tree import math
N = 100005 ;
# Depths of Nodes level = [ 0 for i in range (N)]
LG = 20 ;
# Parent at every 2^i level dp = [[ 0 for j in range (N)]
for i in range (LG)]
# Maximum node at every 2^i level mx = [[ 0 for j in range (N)]
for i in range (LG)]
# Graph that stores destinations # and its weight v = [[] for i in range (N)]
n = 0
# Function to traverse the # nodes using the Depth-First # Search Traversal def dfs_lca(a, par, lev):
dp[ 0 ][a] = par;
level[a] = lev;
for i in v[a]:
# Condition to check
# if its equal to its
# parent then skip
if (i[ 0 ] = = par):
continue ;
mx[ 0 ][i[ 0 ]] = i[ 1 ];
# DFS Recursive Call
dfs_lca(i[ 0 ], a, lev + 1 );
# Function to find the ancestor def find_ancestor():
# Loop to set every 2^i distance
for i in range ( 1 , 16 ):
# Loop to calculate for
# each node in the N-ary tree
for j in range ( 1 , n + 1 ):
dp[i][j] = dp[i - 1 ][dp[i - 1 ][j]];
# Storing maximum edge
mx[i][j] = max (mx[i - 1 ][j],
mx[i - 1 ][dp[i - 1 ][j]]);
def getMax(a, b):
# Swapping if node a is at more depth
# than node b because we will
# always take at more depth
if (level[b] < level[a]):
a, b = b, a
ans = 0 ;
# Difference between the
# depth of the two given
# nodes
diff = level[b] - level[a];
while (diff > 0 ):
log = int (math.log2(diff));
ans = max (ans, mx[log][b]);
# Changing Node B to its
# parent at 2 ^ i distance
b = dp[log][b];
# Subtracting distance by 2^i
diff - = ( 1 << log);
# Take both a, b to its
# lca and find maximum
while (a ! = b):
i = int (math.log2(level[a]));
# Loop to find the maximum 2^ith
# parent the is different
# for both a and b
while (i > 0 and
dp[i][a] = = dp[i][b]):
i - = 1
# Updating ans
ans = max (ans, mx[i][a]);
ans = max (ans, mx[i][b]);
# Changing value to
# its parent
a = dp[i][a];
b = dp[i][b];
return ans;
# Function to compute the Least # common Ancestor def compute_lca():
dfs_lca( 1 , 0 , 0 );
find_ancestor();
# Driver code if __name__ = = "__main__" :
# Undirected tree
n = 5 ;
v[ 1 ].append([ 2 , 2 ]);
v[ 2 ].append([ 1 , 2 ]);
v[ 1 ].append([ 3 , 5 ]);
v[ 3 ].append([ 1 , 5 ]);
v[ 3 ].append([ 4 , 3 ]);
v[ 4 ].append([ 3 , 4 ]);
v[ 3 ].append([ 5 , 1 ]);
v[ 5 ].append([ 3 , 1 ]);
# Computing LCA
compute_lca();
queries = [[ 3 , 5 ], [ 2 , 3 ], [ 2 , 4 ]]
q = 3 ;
for i in range (q):
max_edge = getMax(queries[i][ 0 ],
queries[i][ 1 ]);
print (max_edge)
# This code is contributed by Rutvik_56 |
// C# implementation to find the // maximum weighted edge in the simple // path between two nodes in N-ary Tree using System;
using System.Collections.Generic;
class GFG {
static int N = 100005;
// Depths of Nodes
static int [] level = new int [N];
static int LG = 20;
// Parent at every 2^i level
static int [,] dp = new int [LG, N];
// Maximum node at every 2^i level
static int [,] mx = new int [LG, N];
// Graph that stores destinations
// and its weight
static List<List<Tuple< int , int >>> v = new List<List<Tuple< int , int >>>();
static int n = 0;
// Function to traverse the
// nodes using the Depth-First
// Search Traversal
static void dfs_lca( int a, int par, int lev)
{
dp[0,a] = par;
level[a] = lev;
for ( int i = 0; i < v[a].Count; i++)
{
// Condition to check
// if its equal to its
// parent then skip
if (v[a][i].Item1 == par)
continue ;
mx[0,v[a][i].Item1] = v[a][i].Item2;
// DFS Recursive Call
dfs_lca(v[a][i].Item1, a, lev + 1);
}
}
// Function to find the ancestor
static void find_ancestor()
{
// Loop to set every 2^i distance
for ( int i = 1; i < 16; i++)
{
// Loop to calculate for
// each node in the N-ary tree
for ( int j = 1; j < n + 1; j++)
{
dp[i,j] = dp[i - 1,dp[i - 1,j]];
// Storing maximum edge
mx[i,j] = Math.Max(mx[i - 1,j], mx[i - 1,dp[i - 1,j]]);
}
}
}
static int getMax( int a, int b)
{
// Swapping if node a is at more depth
// than node b because we will
// always take at more depth
if (level[b] < level[a])
{
int temp = a;
a = b;
b = temp;
}
int ans = 0;
// Difference between the
// depth of the two given
// nodes
int diff = level[b] - level[a];
while (diff > 0)
{
int log = ( int )(Math.Log(diff) / Math.Log(2));
ans = Math.Max(ans, mx[log,b]);
// Changing Node B to its
// parent at 2 ^ i distance
b = dp[log,b];
// Subtracting distance by 2^i
diff -= (1 << log);
}
// Take both a, b to its
// lca and find maximum
while (a != b)
{
int i = ( int )(Math.Log(level[a]) / Math.Log(2));
// Loop to find the maximum 2^ith
// parent the is different
// for both a and b
while (i > 0 && dp[i,a] == dp[i,b])
{
i-=1;
}
// Updating ans
ans = Math.Max(ans, mx[i,a]);
ans = Math.Max(ans, mx[i,b]);
// Changing value to
// its parent
a = dp[i,a];
b = dp[i,b];
}
return ans;
}
// Function to compute the Least
// common Ancestor
static void compute_lca()
{
dfs_lca(1, 0, 0);
find_ancestor();
}
static void Main() {
for ( int i = 0; i < LG; i++)
{
for ( int j = 0; j < N; j++)
{
dp[i,j] = 0;
mx[i,j] = 0;
}
}
for ( int i = 0; i < N; i++)
{
v.Add( new List<Tuple< int , int >>());
}
// Undirected tree
v[1].Add( new Tuple< int , int >(2, 2));
v[2].Add( new Tuple< int , int >(1, 2));
v[1].Add( new Tuple< int , int >(3, 5));
v[3].Add( new Tuple< int , int >(1, 5));
v[3].Add( new Tuple< int , int >(4, 3));
v[4].Add( new Tuple< int , int >(3, 4));
v[3].Add( new Tuple< int , int >(5, 1));
v[5].Add( new Tuple< int , int >(3, 1));
// Computing LCA
compute_lca();
int [,] queries
= { { 3, 5 },
{ 2, 3 },
{ 2, 4 } };
int q = 3;
for ( int i = 0; i < q; i++) {
int max_edge = getMax(queries[i,0],
queries[i,1]);
Console.WriteLine(max_edge);
}
}
} // This code is contributed by divyesh072019. |
<script> // Javascript implementation to find the
// maximum weighted edge in the simple
// path between two nodes in N-ary Tree
let N = 100005;
// Depths of Nodes
let level = new Array(N);
level.fill(0);
let LG = 20;
// Parent at every 2^i level
let dp = new Array(LG);
for (let i = 0; i < LG; i++)
{
dp[i] = new Array(N);
for (let j = 0; j < N; j++)
{
dp[i][j] = 0;
}
}
// Maximum node at every 2^i level
let mx = new Array(LG);
for (let i = 0; i < LG; i++)
{
mx[i] = new Array(N);
for (let j = 0; j < N; j++)
{
mx[i][j] = 0;
}
}
// Graph that stores destinations
// and its weight
let v = [];
for (let i = 0; i < N; i++)
{
v.push([]);
}
let n = 0;
// Function to traverse the
// nodes using the Depth-First
// Search Traversal
function dfs_lca(a, par, lev)
{
dp[0][a] = par;
level[a] = lev;
for (let i = 0; i < 2; i++)
{
// Condition to check
// if its equal to its
// parent then skip
if (v[a][0] == par)
continue ;
mx[0][v[a][0]] = v[a][1];
// DFS Recursive Call
dfs_lca(v[a][0], a, lev + 1);
}
}
// Function to find the ancestor
function find_ancestor()
{
// Loop to set every 2^i distance
for (let i = 1; i < 16; i++)
{
// Loop to calculate for
// each node in the N-ary tree
for (let j = 1; j < n + 1; j++)
{
dp[i][j] = dp[i - 1][dp[i - 1][j]];
// Storing maximum edge
mx[i][j] = Math.max(mx[i - 1][j], mx[i - 1][dp[i - 1][j]]);
}
}
}
function getMax(a, b)
{
// Swapping if node a is at more depth
// than node b because we will
// always take at more depth
if (level[b] < level[a])
{
let temp = a;
a = b;
b = temp;
}
let ans = 0;
// Difference between the
// depth of the two given
// nodes
let diff = level[b] - level[a];
while (diff > 0)
{
let log = parseInt(Math.log(diff) / Math.log(2), 10);
ans = Math.max(ans, mx[log][b]);
// Changing Node B to its
// parent at 2 ^ i distance
b = dp[log][b];
// Subtracting distance by 2^i
diff -= (1 << log);
}
// Take both a, b to its
// lca and find maximum
while (a == b)
{
i = parseInt(Math.log(level[a]) / Math.log(2), 10);
// Loop to find the maximum 2^ith
// parent the is different
// for both a and b
while (i > 0 && dp[i][a] == dp[i][b])
{
i-=1;
}
// Updating ans
ans = Math.max(ans, mx[i][a]);
ans = Math.max(ans, mx[i][b]);
// Changing value to
// its parent
a = dp[i][a];
b = dp[i][b];
}
return ans*2 + 1;
}
// Function to compute the Least
// common Ansector
function compute_lca()
{
dfs_lca(1, 0, 0);
find_ancestor();
}
// Undirected tree
n = 5;
v[1].push(2);
v[1].push(2);
v[2].push(1);
v[2].push(2);
v[1].push(3);
v[1].push(5);
v[3].push(1);
v[3].push(5);
v[3].push(4);
v[3].push(3);
v[4].push(3);
v[4].push(4);
v[3].push(5);
v[3].push(1);
v[5].push(3);
v[5].push(1);
// Computing LCA
compute_lca();
let queries= [[3, 5], [2, 3], [2,4]];
let q = 3;
for (let i = 0; i <q; i++)
{
let max_edge = getMax(queries[i][0],
queries[i][1]);
document.write(max_edge + "</br>" );
}
// This code is contributed by suresh07. </script> |
Output:
1 5 5
Time Complexity: O(N*logN).
Auxiliary Space: O(N*logN).