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.
dp[i][j] = dp[i - 1][dp[i - 1][j]] and
mx[i][j] = max(mx[i - 1][j], mx[i - 1][dp[i - 1][j]])
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).