Given an n-ary tree and Q queries where each query consists of an integer u which denotes a node. The task is to print the xor of all the values of nodes in the sub-tree of the given node.
Examples:
Input:
q[] = {0, 1, 4, 5}
Output:
0
3
5
6
Query 1: (1 ^ 2 ^ 3 ^ 4 ^ 5 ^ 6 ^ 7) = 0
Query 2: (2 ^ 4 ^ 5) = 3
Query 3: (5) = 5
Query 4: (6) = 6
Naive approach: For each node we have to traverse the tree for every query find the xor of all the nodes of the sub-tree. So the complexity of the code will be O(N * Q).
Efficient approach: If we pre-compute the xor of all the nodes of the sub-tree by traversing the total tree once and first computing the xor of all the nodes of the sub-tree of the child node first and then using the value of the child node for computing the xor of all the nodes of the sub-tree of the parent node. In this way we can compute the xor in O(N) time and store them for queries. When a query is asked we will print the pre-computed value in O(1) time.
Below is the implementation of the above approach:
// C++ implementation of the approach #include <bits/stdc++.h> using namespace std;
// Adjacency list of the graph vector<vector< int > > graph;
// Value of the node vector< int > values, xor_values;
// Function to pre-compute the xor values int pre_compute_xor( int i, int prev)
{ // xor of the sub-tree
int x = values[i];
for ( int j = 0; j < graph[i].size(); j++)
if (graph[i][j] != prev) {
// xor x with xor of the sub-tree
// of it child nodes
x ^= pre_compute_xor(graph[i][j], i);
}
xor_values[i] = x;
// Return the xor
return x;
} // Function to return the xor of // the nodes of the sub-tree // rooted at node u int query( int u)
{ return xor_values[u];
} // Driver code int main()
{ int n = 7;
graph.resize(n);
xor_values.resize(n);
// Create the graph
graph[0].push_back(1);
graph[0].push_back(2);
graph[1].push_back(3);
graph[1].push_back(4);
graph[2].push_back(5);
graph[2].push_back(6);
// Set the values of the nodes
values.push_back(1);
values.push_back(2);
values.push_back(3);
values.push_back(4);
values.push_back(5);
values.push_back(6);
values.push_back(7);
// Pre-computation
pre_compute_xor(0, -1);
// Perform queries
int queries[] = { 0, 1, 4, 5 };
int q = sizeof (queries) / sizeof (queries[0]);
for ( int i = 0; i < q; i++)
cout << query(queries[i]) << endl;
return 0;
} |
// Java implementation of the approach import java.util.*;
class GFG
{ static int n = 7 ;
// Adjacency list of the graph static Vector<Integer> []graph = new Vector[n];
// Value of the node static Vector<Integer> values = new Vector<Integer>(),
xor_values = new Vector<Integer>(n);
// Function to pre-compute the xor values static int pre_compute_xor( int i, int prev)
{ // xor of the sub-tree
int x = values.get(i);
for ( int j = 0 ; j < graph[i].size(); j++)
if (graph[i].get(j)!= prev)
{
// xor x with xor of the sub-tree
// of it child nodes
x ^= pre_compute_xor(graph[i].get(j), i);
}
xor_values.remove(i);
xor_values.add(i, x);
// Return the xor
return x;
} // Function to return the xor of // the nodes of the sub-tree // rooted at node u static int query( int u)
{ return xor_values.get(u);
} // Driver code public static void main(String[] args)
{ for ( int i = 0 ; i < n; i++)
{
graph[i] = new Vector<Integer>();
xor_values.add( 0 );
}
// Create the graph
graph[ 0 ].add( 1 );
graph[ 0 ].add( 2 );
graph[ 1 ].add( 3 );
graph[ 1 ].add( 4 );
graph[ 2 ].add( 5 );
graph[ 2 ].add( 6 );
// Set the values of the nodes
values.add( 1 );
values.add( 2 );
values.add( 3 );
values.add( 4 );
values.add( 5 );
values.add( 6 );
values.add( 7 );
// Pre-computation
pre_compute_xor( 0 , - 1 );
// Perform queries
int queries[] = { 0 , 1 , 4 , 5 };
int q = queries.length;
for ( int i = 0 ; i < q; i++)
System.out.print(query(queries[i]) + "\n" );
} } // This code is contributed by Rajput-Ji |
# Python3 implementation of the approach # Adjacency list of the graph graph = []
# Value of the node values = []
xor_values = []
# Function to pre-compute the xor values def pre_compute_xor(i, prev):
# xor of the sub-tree
x = values[i]
for j in range ( len (graph[i])):
if graph[i][j] ! = prev:
# xor x with xor of the sub-tree
# of it child nodes
x ^ = pre_compute_xor(graph[i][j], i)
xor_values[i] = x
# Return the xor
return x
# Function to return the xor of # the nodes of the sub-tree # rooted at node u def query(u):
return xor_values[u]
# Driver code n = 7
for i in range (n):
graph.append([])
xor_values.append( 0 )
# Create the graph graph[ 0 ].append( 1 )
graph[ 0 ].append( 2 )
graph[ 1 ].append( 3 )
graph[ 1 ].append( 4 )
graph[ 2 ].append( 5 )
graph[ 2 ].append( 6 )
# Set the values of the nodes values.append( 1 )
values.append( 2 )
values.append( 3 )
values.append( 4 )
values.append( 5 )
values.append( 6 )
values.append( 7 )
# Pre-computation pre_compute_xor( 0 , - 1 )
# Perform queries queries = [ 0 , 1 , 4 , 5 ]
q = len (queries)
for i in range (q):
print (query(queries[i]))
# This code is contributed by divyamohan123 |
// C# implementation of the approach using System;
using System.Collections.Generic;
class GFG
{ static int n = 7;
// Adjacency list of the graph static List< int > []graph = new List< int >[n];
// Value of the node static List< int > values = new List< int >(),
xor_values = new List< int >(n);
// Function to pre-compute the xor values static int pre_compute_xor( int i, int prev)
{ // xor of the sub-tree
int x = values[i];
for ( int j = 0; j < graph[i].Count; j++)
if (graph[i][j] != prev)
{
// xor x with xor of the sub-tree
// of it child nodes
x ^= pre_compute_xor(graph[i][j], i);
}
xor_values.RemoveAt(i);
xor_values.Insert(i, x);
// Return the xor
return x;
} // Function to return the xor of // the nodes of the sub-tree // rooted at node u static int query( int u)
{ return xor_values[u];
} // Driver code public static void Main(String[] args)
{ for ( int i = 0; i < n; i++)
{
graph[i] = new List< int >();
xor_values.Add(0);
}
// Create the graph
graph[0].Add(1);
graph[0].Add(2);
graph[1].Add(3);
graph[1].Add(4);
graph[2].Add(5);
graph[2].Add(6);
// Set the values of the nodes
values.Add(1);
values.Add(2);
values.Add(3);
values.Add(4);
values.Add(5);
values.Add(6);
values.Add(7);
// Pre-computation
pre_compute_xor(0, -1);
// Perform queries
int []queries = { 0, 1, 4, 5 };
int q = queries.Length;
for ( int i = 0; i < q; i++)
Console.Write(query(queries[i]) + "\n" );
} } // This code is contributed by PrinciRaj1992 |
<script> // Javascript implementation of the approach let n = 7; // Adjacency list of the graph let graph = new Array(n);
// Value of the node let values = new Array();
let xor_values = new Array(n);
// Function to pre-compute the xor values function pre_compute_xor(i, prev) {
// xor of the sub-tree
let x = values[i];
for (let j = 0; j < graph[i].length; j++)
if (graph[i][j] != prev) {
// xor x with xor of the sub-tree
// of it child nodes
x ^= pre_compute_xor(graph[i][j], i);
}
xor_values.splice(i, 1);
xor_values.splice(i, 0, x);
// Return the xor
return x;
} // Function to return the xor of // the nodes of the sub-tree // rooted at node u function query(u) {
return xor_values[u];
} // Driver code for (let i = 0; i < n; i++) {
graph[i] = new Array();
xor_values.push(0);
} // Create the graph graph[0].push(1); graph[0].push(2); graph[1].push(3); graph[1].push(4); graph[2].push(5); graph[2].push(6); // Set the values of the nodes values.push(1); values.push(2); values.push(3); values.push(4); values.push(5); values.push(6); values.push(7); // Pre-computation pre_compute_xor(0, -1); // Perform queries let queries = [0, 1, 4, 5]; let q = queries.length; for (let i = 0; i < q; i++)
document.write(query(queries[i]) + "<br>" );
// This code is contributed by gfgking </script> |
0 3 5 6
Time Complexity: O(n), where n is the number of nodes in the graph. This is because the pre_compute_xor function visits each node of the graph exactly once in a depth-first search fashion. The query function takes constant time since it just returns the pre-computed XOR value of the given node.
Auxiliary space: O(n), where n is the number of nodes in the graph. This is because we are using vectors to store the adjacency list of the graph, the values and XOR values of each node. Additionally, the depth of the recursion stack in the pre_compute_xor function can be up to O(n) in the worst case, which contributes to the space complexity.