Count Nodes having Bitwise XOR of all Edges in their Path from the Root Equal to K


Count hubs having Bitwise XOR of all edges in their way from the root equivalent to K We attempt to decide the quantity of hubs in a given tree where the bitwise XOR of all edges along the way from the root to that hub rises to a given worth K. This is known as the issue of counting hubs having the bitwise XOR of all edges in their way from the root equivalent to K. This thrilling point involves productively registering the XOR values along every way from the root to the hubs while crossing the tree. In this article, we take a gander at a few strategies for tackling this issue, including the Trie information structure, Prefix XOR Cluster, and Profundity First Hunt. Every strategy offers unmistakable experiences into successfully counting such hubs and offers supportive methods for settling related issues including trees

Methods Used

  • Depth-First Search (DFS)

  • Prefix XOR Array

  • Trie Data Structure

Depth-First Search (DFS)

With this strategy, we cross the tree utilizing Profundity First Hunt while monitoring the XOR worth of all edges driving from the root to the ongoing hub. The count of each XOR esteem experienced all through the crossing is kept in a hashmap. At every hub, we hope to check whether the hashmap contains the XOR worth of the ongoing way XORed with K. On the off chance that it does, the count is expanded on the grounds that there are hubs on the way whose XOR esteem with the ongoing hub delivers the worth K. The DFS crossing is then kept on looking at additional courses.

Algorithm

  • Set up a hashmap without any preparation to monitor the number of XOR values seen while crossing the DFS.

  • Make a crossing of the tree utilizing profundity first hunt while taking note of the XOR worth of each edge driving from the root to the ongoing hub.

  • Actually look at every hub to check whether the hashmap contains the XOR worth of the ongoing way XORed with K. Provided that this is true, increment the count suitably.

  • Recursively investigate each offspring of the ongoing hub, changing the ongoing way's XOR esteem as required.

  • When the crossing is finished, the count will mirror the quantity of hubs whose bitwise XOR of all edges prompting the root rises to K.

  • O(N), where N is the number of nodes in the tree, is the time complexity

Example

#include <iostream>
#include <unordered_map>
#include <vector>
using namespace std;

unordered_map<int, vector<int>> graph;
unordered_map<int, int> xorCount;

void addEdge(int u, int v) {
   graph[u].push_back(v);
   graph[v].push_back(u);
}

void dfs(int node, int parent, int xorValue, int k) {
   xorValue ^= node;

   if (xorCount.find(xorValue ^ k) != xorCount.end())
      xorCount[xorValue ^ k]++;

   for (int child : graph[node]) {
      if (child != parent) {
         dfs(child, node, xorValue, k);
      }
   }
}

int main() {
   addEdge(1, 2);
   addEdge(1, 3);
   addEdge(2, 4);
   addEdge(2, 5);
   addEdge(3, 6);

   int k = 3;
   xorCount[0] = 1; 
   dfs(1, -1, 0, k); 

   cout << "Count of nodes whose XOR of all edges leading to root equals " << k << ": ";
   cout << xorCount[k] << endl;

   return 0;
}

Output

Count of nodes whose XOR of all edges leading to root equals 3: 0

Sliding Window Approach

In this method, the XOR values of all edges on every path from the root to a specific node are quickly calculated using a prefix XOR array. As we progress down the tree, we calculate the prefix XOR values using a Depth-First Search approach. We determine whether the prefix XOR array has the XOR value of the current path XORed with K for each node. If so, we raise the count appropriately.

Algorithm

  • Create a prefix XOR array from scratch and populate it with the XOR values of all the edges that connect each node to the root.

  • Using a Depth-First Search traverse, descend the tree while refreshing the prefix XOR array.

  • Check each node to see if the prefix XOR array contains the XOR value of the current path XORed with K. If so, increase the count appropriately.

  • The prefix XOR array is updated by recursively exploring every child of the current node.

  • When the crossing is finished, the count will mirror the quantity of hubs whose bitwise XOR of all edges prompting the root approaches K. O(N), where N is the quantity of hubs in the tree, is the time intricacy.

Example

#include <iostream>
#include <vector>
using namespace std;

void dfs(int node, int parent, const vector<vector<int>>& graph, 
vector<int>& prefixXOR, int currentXOR) {
   prefixXOR[node] = currentXOR;
   for (int child : graph[node]) {
      if (child != parent) {
         dfs(child, node, graph, prefixXOR, currentXOR ^ child);
      }
   }
}

void countXORPaths(int node, int parent, const vector<vector<int>>& graph, const vector<int>& prefixXOR, int K, int& count) {
   if (prefixXOR[node] == K)
      count++;
   for (int child : graph[node]) {
      if (child != parent) {
         countXORPaths(child, node, graph, prefixXOR, K, count);
      }
   }
}

int main() {
   int n = 6;
   int root = 0;
   vector<vector<int>> graph(n);
   graph[0] = {1, 2};
   graph[1] = {0, 3, 4};
   graph[2] = {0, 5};
   
   vector<int> prefixXOR(n, 0);
   dfs(root, -1, graph, prefixXOR, 0);

   int K = 2;
   int count = 0;
   countXORPaths(root, -1, graph, prefixXOR, K, count);
   cout << count << endl;

   return 0;
}

Output

2

Trie Data Structure

In this method, the prefix XOR values that are encountered during the traverse are stored in a Trie data structure. We determine whether the Trie contains the XOR value of the current path XORed with K at each node. If so, we raise the count appropriately. The Trie effectively handles XOR operations and enables us to quickly locate the required XOR values.

Algorithm

  • Create a new instance of an empty Trie data structure to hold any prefix XOR values that are encountered during traversing.

  • Perform a Depth-First Search traverse of the tree, updating the Trie as we descend the tree with the prefix XOR values.

  • Check each node to see if the Trie contains the XOR value of the current path XORed with K. If so, increase the count appropriately.

  • Recursively examine each of the current node's offspring while updating the Trie.

  • When the crossing is finished, the count will mirror the quantity of hubs whose bitwise XOR of all edges prompting the root approaches K.

  • O(N), where N is the quantity of hubs in the tree, is the time intricacy.

Example

#include <iostream>
#include <unordered_map>
#include <vector>
using namespace std;

struct TrieNode {
   unordered_map<int, TrieNode*> children;
};

class Trie {
public:
   Trie() {
      root = new TrieNode();
   }

   void insert(int num) {
      TrieNode* curr = root;
      for (int i = 31; i >= 0; --i) {
         int bit = (num >> i) & 1;
         if (curr->children.find(bit) == curr->children.end())
            curr->children[bit] = new TrieNode();
         curr = curr->children[bit];
      }
   }

   bool search(int num) {
      TrieNode* curr = root;
      for (int i = 31; i >= 0; --i) {
         int bit = (num >> i) & 1;
         if (curr->children.find(1 - bit) != curr->children.end())
            curr = curr->children[1 - bit];
         else
            curr = curr->children[bit];
      }
      return true;
   }

private:
   TrieNode* root;
};

struct TreeNode {
   int val;
   vector<TreeNode*> children;
};

int countPrefixXOR(TreeNode* root, Trie& trie, int curXOR, int targetXOR) {
   curXOR ^= root->val;
   trie.insert(curXOR);
   int count = trie.search(curXOR ^ targetXOR);
   for (TreeNode* child : root->children) {
      count += countPrefixXOR(child, trie, curXOR, targetXOR);
   }
   return count;
}

int main() {
   // Sample Tree Creation
   TreeNode* root = new TreeNode();
   root->val = 1;

   TreeNode* node1 = new TreeNode();
   node1->val = 0;
   root->children.push_back(node1);

   TreeNode* node2 = new TreeNode();
   node2->val = 1;
   root->children.push_back(node2);

   TreeNode* node3 = new TreeNode();
   node3->val = 0;
   node1->children.push_back(node3);

   TreeNode* node4 = new TreeNode();
   node4->val = 1;
   node1->children.push_back(node4);

   TreeNode* node5 = new TreeNode();
   node5->val = 1;
   node2->children.push_back(node5);

   int K = 5;

   Trie trie;
   int result = countPrefixXOR(root, trie, 0, K);
   cout << "The count of nodes with prefix XOR equal to K is: " << 
result << endl;

   return 0;
}

Output

The count of nodes with prefix XOR equal to K is: 6

Conclusion

All three methods effectively count the nodes that have a bitwise XOR of K or greater for all of the edges on their journey from the root. The method chosen is determined by the particular requirements, the accessible data structures, and the size of the tree. The DFS technique is straightforward and simple to use, however it might not be the best for huge trees. Performance-critical applications favor the Prefix XOR Array and Trie methods because they are more effective for larger trees and provide constant-time XOR operations.

Updated on: 04-Aug-2023

79 Views

Kickstart Your Career

Get certified by completing the course

Get Started
Advertisements