Find edge to be deleted from Tree to maximise product of XOR of components
Given a tree of having N nodes rooted at node 0, and an array val[] denoting the value at each node, the task is to find the maximum possible product of XOR of connected components after removing one edge from the tree and also the edge which is removed.
Note: If there are multiple edges that give the maximum value then print them all in any order.
Examples:
Input: edges[][] = { {0, 5}, {0, 4}, {5, 1}, {5, 3}, {5, 2} }, val[ ] = { 7, 15, 9, 1, 1, 12}
Output: max_xor_product = 66
Edges: {0, 5}
Explanation: If we delete an edge {0, 5} then the tree will divide into two components.
The XOR of the first component is (1^7) = 6.
And the XOR of the second component is ( 12^15^1^9) = 11.
The product of xor of those components is ( 6*11) = 66.
Similarly, if we delete an edge { 5, 1} tree again divide into two-component.
The XOR of the first component is ( 15 ) ( because it has only one node)
and XOR of the second component is ( 7^1^12^9^1) = 2.
And the product of XOR of those components is ( 15 * 2 ) = 30.
If this is repeated the maximum value of the product of XOR of components will be 66,
which can be achieved if we delete the edge {0, 5}.See the image below to understand it better
Input: edges[][] = { {0, 1}, {0, 2}}, val[ ]={ 17, 17, 17}
Output: max_xor_product = 0
Edges: {{0, 1}, {0, 2}}
Naive Approach: Delete each edge and traverse through components and take xor of values of nodes in those components, then do the product of xor, store the maximum value and the edge corresponding to that value.
Time Complexity: O(N*N)
Auxiliary Space: O(N)
Efficient Method: This problem can be solved by precomputing XOR of subtree of each node and using bitwise XOR property as follows:
If an edge of a tree is deleted, then the tree will always divide into two components. The XOR of one component will be same as the XOR of the subtree (say X) and the other component will have XOR = (total XOR of all nodes ^ X).
For each edge, consider it to be removed and then find the XOR of both the components (using above observation) and their product. Keep track of the maximum and removing which edge results in that.
Follow the illustration shown below for a better understanding.
Illustration:
Consider the first example given below
edges[][] = { {0, 5}, {0, 4}, {5, 1}, {5, 3}, {5, 2} }, val[ ] = { 7, 15, 9, 1, 1, 12}Remove edge {0, 4}:
=> Two components are {1} and {7, 12, 15, 1, 9}
=> XOR values are 1 and 12
=> Product = 1*12 = 12Remove edge {0, 5}:
=> Two components are {1, 7} and {12, 15, 1, 9}
=> XOR values are 6 and 11
=> Product = 6*11 = 66Remove edge {5, 1}:
=> Two components are {1, 7, 12, 1, 9} and {15}
=> XOR values are 2 and 15
=> Product = 2*15 = 30Remove edge {5, 2}:
=> Two components are {1, 7, 12, 15, 1} and {9}
=> XOR values are 4 and 9
=> Product = 4*9 = 36Remove edge {5, 3}:
=> Two components are {1, 7, 12, 15, 9} and {1}
=> XOR values are 1 and 12
=> Product = 1*12 = 12So the maximum value is 66 which is achieved when the edge {0, 5} is removed
Follow the below steps to solve this problem:
- Calculate the XOR of all given values of all tree nodes (say tot_xor)
- Create an array (say subtree_xor[])and store the bitwise xor of the subtree of ith node using DFS.
- Now travel through the tree using DFS and for each node:
- Consider the edge between current node and its parent is removed.
- The two components will be: current node with its subtree and the remaining of the tree.
- Calculate the bitwise xor of current node with its subtree and of the remaining tree as mentioned in the above observation.
- Find the product of the XOR values.
- Update maximum value and removed edges accordingly.
- Return the maximum value and the removed edges.
Below is the implementation of the above approach:
C++
// C++ code to implement the approach #include <bits/stdc++.h> using namespace std; const int mxx = 1e6 + 7; int subtree_xor[mxx]; unordered_map< long long , vector<pair< int , int > > > store; // To add edges in tree void addEdge(vector< int > tree[], int u, int v) { tree[u].push_back(v); tree[v].push_back(u); } // To precompute xor value of each subtree void dfs(vector< int > tree[], int val[], int cur_node, int par) { // assign value of current node subtree_xor[cur_node] = val[cur_node]; for ( auto & child : tree[cur_node]) { if (child == par) continue ; dfs(tree, val, child, cur_node); // take xor of all child node subtree_xor[cur_node] ^= subtree_xor[child]; } } // To store all xor_product // and it's corresponding edges void store_xor_product(vector< int > tree[], int cur_node, int par, int tot_xor) { for ( auto & child : tree[cur_node]) { if (child == par) continue ; // Xor of first component int first_comp_xor = subtree_xor[child]; // Xor of second component int second_comp_xor = tot_xor ^ first_comp_xor; // Product can exceed int range // so store it in long long data type long long xor_product = first_comp_xor * 1LL * second_comp_xor; // Store edges corresponding // to its product store[xor_product].push_back({ cur_node, child }); store_xor_product(tree, child, cur_node, tot_xor); } } // To print edges corresponding // to max_xor_product of components void print_edges( long long mx_product) { for ( auto edges : store[mx_product]) { cout << edges.first << " " << edges.second << "\n" ; } } // Driver code int findVal( int N, int val[], vector<vector< int > >& edges) { int tot_xor = 0; // Store the xor of all values for ( int i = 0; i < N; i++) tot_xor ^= val[i]; vector< int > tree[N]; // Create a tree from given edges for ( int i = 0; i < N - 1; i++) addEdge(tree, edges[i][0], edges[i][1]); // Dfs travel to store subtree xor dfs(tree, val, 0, -1); // To store edges corresponding // to xor_product store_xor_product(tree, 0, -1, tot_xor); // Find maximum xor_product long long mx_product = -1; for ( auto ele : store) { long long cur_product = ele.first; mx_product = max(mx_product, cur_product); } return mx_product; } // Driver code int main() { int N = 6; vector<vector< int > > edges = { { 0, 5 }, { 0, 4 }, { 5, 1 }, { 5, 3 }, { 5, 2 } }; int val[] = { 7, 15, 9, 1, 1, 12 }; int mx_product = findVal(N, val, edges); cout << mx_product << "\n" ; // To print edges corresponding // to maximum xor_product print_edges(mx_product); return 0; } |
Java
// Java code for the above approach: import java.util.*; public class Main { static int mxx = 1000007 ; static int subtree_xor[]; static HashMap <Long, ArrayList <IntPair > > store; static class IntPair { int first; int second; IntPair( int x, int y) { this .first = x; this .second = y; } } // To add edges in tree static void addEdge(ArrayList <ArrayList <Integer>> tree, int u, int v) { tree.get(u).add(v); tree.get(v).add(u); } // To precompute xor value of each subtree static void dfs(ArrayList <ArrayList <Integer>> tree, int val[], int cur_node, int par) { // assign value of current node subtree_xor[cur_node] = val[cur_node]; for (Integer child : tree.get(cur_node)) { if (child == par) continue ; dfs(tree, val, child, cur_node); // take xor of all child node subtree_xor[cur_node] ^= subtree_xor[child]; } } // To store all xor_product // and it's corresponding edges static void store_xor_product(ArrayList <ArrayList <Integer>> tree, int cur_node, int par, int tot_xor) { for (Integer child : tree.get(cur_node)) { if (child == par) continue ; // Xor of first component long first_comp_xor = subtree_xor[child]; // Xor of second component long second_comp_xor = tot_xor ^ first_comp_xor; // Product can exceed int range // so store it in long long data type long xor_product = first_comp_xor * second_comp_xor; // Store edges corresponding // to its product if ( ! store.containsKey(xor_product) ){ store.put(xor_product, new ArrayList<IntPair> ()); } store.get(xor_product).add( new IntPair(cur_node, child) ); store_xor_product(tree, child, cur_node, tot_xor); } } // To print edges corresponding // to max_xor_product of components static void print_edges( long mx_product) { if (store.containsKey(mx_product) ) { for (IntPair edges : store.get(mx_product) ) { System.out.println(edges.first + " " + edges.second ); } } } static long findVal( int N, int val[], int [][] edges) { int tot_xor = 0 ; // Store the xor of all values for ( int i = 0 ; i < N; i++) tot_xor ^= val[i]; ArrayList <ArrayList <Integer> > tree = new ArrayList <ArrayList <Integer>> (N); for ( int i = 0 ; i < N; i++){ tree.add( new ArrayList <Integer>()); } // Create a tree from given edges for ( int i = 0 ; i < N - 1 ; i++) addEdge(tree, edges[i][ 0 ], edges[i][ 1 ]); // Dfs travel to store subtree xor dfs(tree, val, 0 , - 1 ); // To store edges corresponding // to xor_product store_xor_product(tree, 0 , - 1 , tot_xor); // Find maximum xor_product long mx_product = - 1 ; for (HashMap.Entry < Long, ArrayList <IntPair> > ele : store.entrySet()) { long cur_product = ele.getKey(); mx_product = Math.max(mx_product, cur_product); } return mx_product; } // Driver Code public static void main(String args[]) { int N = 6 ; int [][] edges = { { 0 , 5 }, { 0 , 4 }, { 5 , 1 }, { 5 , 3 }, { 5 , 2 } }; subtree_xor = new int [mxx]; store = new HashMap <Long, ArrayList <IntPair > > (); int val[] = { 7 , 15 , 9 , 1 , 1 , 12 }; long mx_product = findVal(N, val, edges); System.out.println( mx_product); // To print edges corresponding // to maximum xor_product print_edges(mx_product); } } // This code is contributed by Sachin Sahara (sachin801) |
Python3
# Python code to implement the approach mxx = 1000007 subtree_xor = [ 0 ] * mxx store = {} class IntPair: def __init__( self , x, y): self .first = x self .second = y # To add edges in tree def addEdge(tree, u, v): tree[u].append(v) tree[v].append(u) # To precompute xor value of each subtree def dfs(tree, val, cur_node, par): # assign value of current node subtree_xor[cur_node] = val[cur_node] for child in tree[cur_node]: if child = = par: continue dfs(tree, val, child, cur_node) # take xor of all child node subtree_xor[cur_node] ^ = subtree_xor[child] # To store all xor_product # and it's corresponding edges def store_xor_product(tree, cur_node, par, tot_xor): for child in tree[cur_node]: if child = = par: continue # Xor of first component first_comp_xor = subtree_xor[child] # Xor of second component second_comp_xor = tot_xor ^ first_comp_xor # Product can exceed int range # so store it in long long data type xor_product = first_comp_xor * second_comp_xor # Store edges corresponding to its product if xor_product not in store: store[xor_product] = [] store[xor_product].append(IntPair(cur_node, child)) store_xor_product(tree, child, cur_node, tot_xor) # To print edges corresponding # to max_xor_product of components def print_edges(mx_product): if mx_product in store: for edges in store[mx_product]: print (edges.first, edges.second) def findVal(N, val, edges): tot_xor = 0 # Store the xor of all values for i in range (N): tot_xor ^ = val[i] tree = [[] for i in range (N)] # Create a tree from given edges for i in range (N - 1 ): addEdge(tree, edges[i][ 0 ], edges[i][ 1 ]) # Dfs travel to store subtree xor dfs(tree, val, 0 , - 1 ) # To store edges corresponding # to xor_product store_xor_product(tree, 0 , - 1 , tot_xor) # Find maximum xor_product mx_product = - 1 for cur_product in store: mx_product = max (mx_product, cur_product) return mx_product N = 6 edges = [ [ 0 , 5 ], [ 0 , 4 ], [ 5 , 1 ], [ 5 , 3 ], [ 5 , 2 ], ] val = [ 7 , 15 , 9 , 1 , 1 , 12 ] mx_product = findVal(N, val, edges) print (mx_product) # To print edges corresponding # to maximum xor_product print_edges(mx_product) |
C#
// C# code for the above approach: using System; using System.Collections.Generic; public class GFG { static int mxx = 1000007; static int [] subtree_xor; static Dictionary< long , List<IntPair> > store; class IntPair { public int first; public int second; public IntPair( int x, int y) { this .first = x; this .second = y; } } // To add edges in tree static void addEdge(List<List< int > > tree, int u, int v) { tree[u].Add(v); tree[v].Add(u); } // To precompute xor value of each subtree static void dfs(List<List< int > > tree, int [] val, int cur_node, int par) { // assign value of current node subtree_xor[cur_node] = val[cur_node]; foreach ( int child in tree[cur_node]) { if (child == par) continue ; dfs(tree, val, child, cur_node); // take xor of all child node subtree_xor[cur_node] ^= subtree_xor[child]; } } // To store all xor_product // and it's corresponding edges static void store_xor_product(List<List< int > > tree, int cur_node, int par, int tot_xor) { foreach ( int child in tree[cur_node]) { if (child == par) continue ; // Xor of first component long first_comp_xor = subtree_xor[child]; // Xor of second component long second_comp_xor = tot_xor ^ first_comp_xor; // Product can exceed int range // so store it in long long data type long xor_product = first_comp_xor * second_comp_xor; // Store edges corresponding // to its product if (!store.ContainsKey(xor_product)) { store.Add(xor_product, new List<IntPair>()); } store[xor_product].Add( new IntPair(cur_node, child)); store_xor_product(tree, child, cur_node, tot_xor); } } // To print edges corresponding // to max_xor_product of components static void print_edges( long mx_product) { if (store.ContainsKey(mx_product)) { foreach (IntPair edges in store[mx_product]) { Console.WriteLine(edges.first + " " + edges.second); } } } static long findVal( int N, int [] val, int [][] edges) { int tot_xor = 0; // Store the xor of all values for ( int i = 0; i < N; i++) tot_xor ^= val[i]; List<List< int > > tree = new List<List< int > >(N); for ( int i = 0; i < N; i++) { tree.Add( new List< int >()); } // Create a tree from given edges for ( int i = 0; i < N - 1; i++) addEdge(tree, edges[i][0], edges[i][1]); // Dfs travel to store subtree xor dfs(tree, val, 0, -1); // To store edges corresponding // to xor_product store_xor_product(tree, 0, -1, tot_xor); // Find maximum xor_product long mx_product = -1; foreach ( KeyValuePair< long , List<IntPair> > ele in store) { long cur_product = ele.Key; mx_product = Math.Max(mx_product, cur_product); } return mx_product; } // Driver Code static public void Main() { int N = 6; int [][] edges = { new int [] { 0, 5 }, new int [] { 0, 4 }, new int [] { 5, 1 }, new int [] { 5, 3 }, new int [] { 5, 2 } }; subtree_xor = new int [mxx]; store = new Dictionary< long , List<IntPair> >(); int [] val = { 7, 15, 9, 1, 1, 12 }; long mx_product = findVal(N, val, edges); Console.WriteLine(mx_product); // To print edges corresponding // to maximum xor_product print_edges(mx_product); } } // This code is contributed by akashish__ |
Javascript
// JavaScript code for the above approach const mxx = 1000007; let subtree_xor = new Array(mxx); let store = new Map(); class IntPair { constructor(x, y) { this .first = x; this .second = y; } } // To add edges in tree function addEdge(tree, u, v) { tree[u].push(v); tree[v].push(u); } // To precompute xor value of each subtree function dfs(tree, val, cur_node, par) { // assign value of current node subtree_xor[cur_node] = val[cur_node]; for (const child of tree[cur_node]) { if (child === par) continue ; dfs(tree, val, child, cur_node); // take xor of all child node subtree_xor[cur_node] ^= subtree_xor[child]; } } // To store all xor_product // and it's corresponding edges function store_xor_product(tree, cur_node, par, tot_xor) { for (const child of tree[cur_node]) { if (child === par) continue ; // Xor of first component const first_comp_xor = subtree_xor[child]; // Xor of second component const second_comp_xor = tot_xor ^ first_comp_xor; // Product can exceed int range // so store it in long long data type const xor_product = first_comp_xor * second_comp_xor; // Store edges corresponding to its product if (!store.has(xor_product)) { store.set(xor_product, []); } store.get(xor_product).push( new IntPair(cur_node, child)); store_xor_product(tree, child, cur_node, tot_xor); } } // To print edges corresponding // to max_xor_product of components function print_edges(mx_product) { if (store.has(mx_product)) { for (const edges of store.get(mx_product)) { console.log(edges.first + " " + edges.second); } } } function findVal(N, val, edges) { let tot_xor = 0; // Store the xor of all values for (let i = 0; i < N; i++) tot_xor ^= val[i]; const tree = new Array(N); for (let i = 0; i < N; i++) { tree[i] = []; } // Create a tree from given edges for (let i = 0; i < N - 1; i++) { addEdge(tree, edges[i][0], edges[i][1]); } // Dfs travel to store subtree xor dfs(tree, val, 0, -1); // To store edges corresponding // to xor_product store_xor_product(tree, 0, -1, tot_xor); // Find maximum xor_product let mx_product = -1; for (const ele of store.entries()) { const cur_product = ele[0]; mx_product = Math.max(mx_product, cur_product); } return mx_product; } const N = 6; const edges = [ [0, 5], [0, 4], [5, 1], [5, 3], [5, 2], ]; const val = [7, 15, 9, 1, 1, 12]; const mx_product = findVal(N, val, edges); console.log(mx_product); // To print edges corresponding // to maximum xor_product print_edges(mx_product); // This code is contributed by Potta Lokesh |
66 0 5
Time Complexity: O( N )
Auxiliary Space: O( N )
Please Login to comment...