Check if XOR of each connected component becomes equal after removing atmost P edges

Last Updated : 9 Dec, 2022

Given, a Tree with N nodes, and an integer P, the task is to remove at edges in range [1, P) and find the XOR of nodes for every connected component formed. If node's values come out to be equal for all connected components formed, Print "YES" else "NO".

Examples:

Input: N = 5, P = 5, Edges[][]={ { 1, 2 }, { 2, 3 }, { 1, 4 }, { 4, 5 }}, nodes[] = {2, 2, 2, 2, 2}

Output: YES
Explanation: We can remove all edges, then there will be 5 connected components each having one node with value 2, thus XOR of each of them will be 2.
 

Input: N = 5,  P = 2, Edges[][]={ { 1, 2 }, { 2, 3 }, { 1, 4 }, { 4, 5 }}, nodes[] = {1, 6, 4, 1, 2}

Output: YES
Explanation: As, p = 2, atleast one edge need to be removed so, removing edge (4, 5), gives two connected components.
XOR of first component would be, arr1 XOR  arr2 XOR arr3 XOR arr4= 1 XOR 6 XOR 4 XOR 1 => 2.
XOR of second component will be arr5 = 2. (Thus, both equal).

 

Approach: The approach to solve this problem is based on following observations:

The fact here, is that deletion of edges should be either once or twice, as:

  • Removing one edge gives us 2 connected components, which should have same XOR and thus by properties of xor if XOR1 == XOR2, then XOR1 ^ XOR2 = 0, where ^ is the bitwise XOR.
  • Similarly, if XOR of the whole tree is 0, removing any edge, will give 2 connected components where both components would have equal value, thus answer = "YES".
  • Now, if we delete 2 edges, deleting more than that would just merge the other components with each other. For ex: if there are 4 or more connected components, we can reduce and merge them, as for 4 it can be reduced to 2 components (which will fall in the case if we remove 1 edge).
  • Now, deleting 2 edges gives us a minimum of 3 connected components, using properties of xor we know, that XOR1 ^ XOR2 ^ XOR3 = let's say some value(y), which means we need to find such subtrees(or say search for 2 edges to remove) whose XOR is equal to some value(y), and if we find it then answer = "YES" else "NO", such that p>2.
  • For ex: say, if we have total XOR = some value(y), with N = 4, there can be 3 segments whose XOR would have been y, then the total elements could get XOR = y, such that p > 2.
    So, overall it can be said that if we find two subtrees whose XOR = y, and if our total XOR was y, we can say that our left out component/segment XOR would also be y, thus answer = "YES", such that p > 2, using above property XOR1 ^ XOR2 ^ XOR3 = some value(y),

Follow the steps below to apply the above approach:

To find such subtrees, with a minimum of 3 connected components, it can be done easily using DFS traversal and during backtracking, we will store each index XOR at each iteration. 

Below is the implementation of the above approach:
 

C++
// C++ code for Check after removing
// edges bitwise XOR of each connected
// component formed is equal

#include <bits/stdc++.h>
using namespace std;

const int N = 1e5;

// adjacency list to form a tree
vector<int> adj[N];

// array arr defined to store node values
// array cal_XOR defined to store
// xor values rooted at i'th level
int nodes[N], cal_XOR[N];

// defined a visited array
vector<bool> visited(N);

// defined a counter to store
// number of subtrees having value equal
// to whole tree XOR
int counter = 0;

// first dfs function to find
// xor of subtrees
int dfs_First(int x)
{
    // initializing cal_XOR
    // array with tree node's value
    cal_XOR[x] = nodes[x];

    // marking each visited node as true
    visited[x] = true;

    // iterating through current node children
    for (auto y : adj[x]) {
        // check if node->child is not visited
        if (!visited[y]) {
            // computing xor of subtree rooted at x
            cal_XOR[x] ^= dfs_First(y);
        }
    }

    // returning xor of subtree rooted at x
    return cal_XOR[x];
}

// second dfs function to count
// subtrees having values equal
// to entire XOR of tree nodes values
// and returning subtree XOR till that point
int dfs_Second(int x)
{

    // marking each visited node as true
    visited[x] = true;

    // storing value of nodes[x]
    // to another variable temp
    // for further calculation
    int temp = nodes[x];

    // iterating through current node children
    for (auto y : adj[x]) {

        // check if node->child is not visited
        if (!visited[y]) {

            // storing xor of subtree
            temp ^= dfs_Second(y);
        }
    }

    // now, checking if xor of subtree
    // computed till now is equal to
    // entire XOR of tree (root)
    // i.e. value at cal_XOR[0] ->
    // which will give entire XOR of the tree
    if (temp == cal_XOR[0]) {
        // then, make that xor 0
        temp = 0;

        // increase count by 1, which
        // means we found a subtree
        counter++;
    }

    // return xor of subtree
    // till that point
    return temp;
}
// Function to add edges
void addEdge(int u, int v)
{
    adj[u].push_back(v);
    adj[v].push_back(u);
}
// Function to take input
// for (n-1) edges
void init(int edges[4][2], int numEdges)
{
    for (int i = 0; i < numEdges; i++) {
        edges[i][0]--;
        edges[i][1]--;
        addEdge(edges[i][0], edges[i][1]);
    }
}

// Driver Code
int main()
{

    // taking input
    int n = 5, p = 2;
    nodes[0] = 1, nodes[1] = 6, nodes[2] = 4,
    nodes[3] = 1, nodes[4] = 2;

    // making our visited array false
    for (int i = 0; i < n; i++) {
        visited[i] = false;
    }

    // taking input for (n-1) edges
    int edges[4][2]
        = { { 1, 2 }, { 2, 3 }, { 1, 4 }, { 4, 5 } };
    init(edges, 4);

    // First dfs Function call
    dfs_First(0);

    // again, marking visited array to false
    for (int i = 0; i < n; i++) {
        visited[i] = false;
    }

    // initializing answer variable
    bool answer = false;

    // if we found XOR of entire tree
    // equal to zero, answer = "YES", as
    // it means there are two connected
    // components equal to each other
    // thus, a single edge can be removed
    if (cal_XOR[0] == 0) {
        answer = true;
    }
    else {
        // second DFS function call
        dfs_Second(0);

        // if we found 2 subtree having
        // equal value with XOR of entire tree
        // answer is always "YES", such that
        // p > 2
        if (counter >= 2 and p != 2) {
            answer = true;
        }
    }
    // printing the final answer
    if (answer == true) {
        cout << "YES"
             << "\n";
    }
    else {
        cout << "NO"
             << "\n";
    }

    // making counter = 0 for next iteration
    counter = 0;

    for (int i = 0; i < n; i++) {

        // similarly clearing adjacency list
        // and both arr and cal_XOR array
        adj[i].clear();
        cal_XOR[i] = nodes[i] = 0;
    }
}
Java
// Java code for Check after removing
// edges bitwise XOR of each connected
// component formed is equal
import java.util.*;

public class GFG {
    static int N = 100000;

    // adjacency list to form a tree
    static ArrayList<ArrayList<Integer> > adj
        = new ArrayList<>();

    // array arr defined to store node values
    // array cal_XOR defined to store
    // xor values rooted at i'th level
    static int[] nodes = new int[N];
    static int[] cal_XOR = new int[N];

    // defined a visited array
    static boolean[] visited = new boolean[N];

    // defined a counter to store
    // number of subtrees having value equal
    // to whole tree XOR
    static int counter = 0;

    // first dfs function to find
    // xor of subtrees
    static int dfs_First(int x)
    {
      
        // initializing cal_XOR
        // array with tree node's value
        cal_XOR[x] = nodes[x];

        // marking each visited node as true
        visited[x] = true;

        // iterating through current node children
        for (Integer y : adj.get(x)) 
        {
          
            // check if node->child is not visited
            if (!visited[y]) 
            {
              
                // computing xor of subtree rooted at x
                cal_XOR[x] ^= dfs_First(y);
            }
        }

        // returning xor of subtree rooted at x
        return cal_XOR[x];
    }

    // second dfs function to count
    // subtrees having values equal
    // to entire XOR of tree nodes values
    // and returning subtree XOR till that point
    static int dfs_Second(int x)
    {

        // marking each visited node as true
        visited[x] = true;

        // storing value of nodes[x]
        // to another variable temp
        // for further calculation
        int temp = nodes[x];

        // iterating through current node children
        for (Integer y : adj.get(x)) {

            // check if node->child is not visited
            if (!visited[y]) {

                // storing xor of subtree
                temp ^= dfs_Second(y);
            }
        }

        // now, checking if xor of subtree
        // computed till now is equal to
        // entire XOR of tree (root)
        // i.e. value at cal_XOR[0] ->
        // which will give entire XOR of the tree
        if (temp == cal_XOR[0])
        {
          
            // then, make that xor 0
            temp = 0;

            // increase count by 1, which
            // means we found a subtree
            counter++;
        }

        // return xor of subtree
        // till that point
        return temp;
    }

    // Function to add edges
    static void addEdge(int u, int v)
    {
        adj.get(u).add(v);
        adj.get(v).add(u);
    }
    // Function to take input
    // for (n-1) edges
    static void init(int[][] edges, int numEdges)
    {
        for (int i = 0; i < numEdges; i++) {
            edges[i][0]--;
            edges[i][1]--;
            addEdge(edges[i][0], edges[i][1]);
        }
    }

    // Driver Code
    public static void main(String[] args)
    {
      
        // taking input
        int n = 5, p = 2;
        nodes[0] = 1;
        nodes[1] = 6;
        nodes[2] = 4;
        nodes[3] = 1;
        nodes[4] = 2;
        for (int i = 0; i < n; i++) {
            adj.add(new ArrayList<>());
        }
      
        // taking input for (n-1) edges
        int[][] edges
            = { { 1, 2 }, { 2, 3 }, { 1, 4 }, { 4, 5 } };
        init(edges, 4);

        // First dfs Function call
        dfs_First(0);

        // again, marking visited array to false
        for (int i = 0; i < n; i++) {
            visited[i] = false;
        }

        // initializing answer variable
        boolean answer = false;
      
        // if we found XOR of entire tree
        // equal to zero, answer = "YES", as
        // it means there are two connected
        // components equal to each other
        // thus, a single edge can be removed
        if (cal_XOR[0] == 0) {
            answer = true;
        }
        else {
            // second DFS function call
            dfs_Second(0);

            // if we found 2 subtree having
            // equal value with XOR of entire tree
            // answer is always "YES", such that
            // p > 2
            if (counter >= 2 && p != 2) {
                answer = true;
            }
        }
        // printing the final answer
        if (answer == true) {
            System.out.println("YES");
        }
        else {
            System.out.println("NO");
        }

        // making counter = 0 for next iteration
        counter = 0;

        for (int i = 0; i < n; i++) {

            // similarly clearing adjacency list
            // and both arr and cal_XOR array
            adj.get(i).clear();
            cal_XOR[i] = nodes[i] = 0;
        }
    }
}

// This code is contributed by karandeep1234.
Python3
# python3 code for Check after removing
# edges bitwise XOR of each connected
# component formed is equal
N = int(1e5)

# adjacency list to form a tree
adj = [[] for _ in range(N)]

# array arr defined to store node values
# array cal_XOR defined to store
# xor values rooted at i'th level
nodes, cal_XOR = [0 for _ in range(N)], [0 for _ in range(N)]

# defined a visited array
visited = [0 for _ in range(N)]

# defined a counter to store
# number of subtrees having value equal
# to whole tree XOR
counter = 0

# first dfs function to find
# xor of subtrees
def dfs_First(x):
    global N, adj, nodes, cal_XOR, visited, counter
    
    # initializing cal_XOR
    # array with tree node's value
    cal_XOR[x] = nodes[x]

    # marking each visited node as true
    visited[x] = True

    # iterating through current node children
    for y in adj[x]:
                # check if node->child is not visited
        if (not visited[y]):
                        # computing xor of subtree rooted at x
            cal_XOR[x] ^= dfs_First(y)

        # returning xor of subtree rooted at x
    return cal_XOR[x]


# second dfs function to count
# subtrees having values equal
# to entire XOR of tree nodes values
# and returning subtree XOR till that point
def dfs_Second(x):

    global N, adj, nodes, cal_XOR, visited, counter

    # marking each visited node as true
    visited[x] = True

    # storing value of nodes[x]
    # to another variable temp
    # for further calculation
    temp = nodes[x]

    # iterating through current node children
    for y in adj[x]:

                # check if node->child is not visited
        if (not visited[y]):

                        # storing xor of subtree
            temp ^= dfs_Second(y)

        # now, checking if xor of subtree
        # computed till now is equal to
        # entire XOR of tree (root)
        # i.e. value at cal_XOR[0] ->
        # which will give entire XOR of the tree
    if (temp == cal_XOR[0]):
                # then, make that xor 0
        temp = 0

        # increase count by 1, which
        # means we found a subtree
        counter += 1

        # return xor of subtree
        # till that point
    return temp

# Function to add edges
def addEdge(u, v):
    global N, adj, nodes, cal_XOR, visited, counter
    adj[u].append(v)
    adj[v].append(u)

# Function to take input
# for (n-1) edges
def init(edges, numEdges):
    global N, adj, nodes, cal_XOR, visited, counter
    for i in range(0, numEdges):
        edges[i][0] -= 1
        edges[i][1] -= 1
        addEdge(edges[i][0], edges[i][1])

# Driver Code
if __name__ == "__main__":

        # taking input
    n, p = 5, 2
    nodes[0], nodes[1], nodes[2] = 1, 6, 4
    nodes[3], nodes[4] = 1, 2

    # making our visited array false
    for i in range(0, n):
        visited[i] = False

    # taking input for (n-1) edges
    edges = [[1, 2], [2, 3], [1, 4], [4, 5]]
    init(edges, 4)

    # First dfs Function call
    dfs_First(0)

    # again, marking visited array to false
    for i in range(0, n):
        visited[i] = False

    # initializing answer variable
    answer = False

    # if we found XOR of entire tree
    # equal to zero, answer = "YES", as
    # it means there are two connected
    # components equal to each other
    # thus, a single edge can be removed
    if (cal_XOR[0] == 0):
        answer = True

    else:
        # second DFS function call
        dfs_Second(0)

        # if we found 2 subtree having
        # equal value with XOR of entire tree
        # answer is always "YES", such that
        # p > 2
        if (counter >= 2 and p != 2):
            answer = True

    # printing the final answer
    if (answer == True):
        print("YES")

    else:
        print("NO")

    # making counter = 0 for next iteration
    counter = 0

    for i in range(0, n):

        # similarly clearing adjacency list
        # and both arr and cal_XOR array
        adj[i] = []
        cal_XOR[i] = nodes[i] = 0

    # This code is contributed by rakeshsahni
C#
// C# code for Check after removing 
// edges bitwise XOR of each connected 
// component formed is equal 
using System; 
using System.Collections; 
using System.Collections.Generic; 

public class GFG 
{ 
    // adjacency list to form a tree 
    static List<int>[] adj = new List<int>[10000]; 

    // array arr defined to store node values 
    // array cal_XOR defined to store 
    // xor values rooted at i'th level 
    static int[] nodes = new int[10000]; 
    static int[] cal_XOR = new int[10000]; 

    // defined a visited array 
    static bool[] visited = new bool[10000]; 

    // defined a counter to store 
    // number of subtrees having value equal 
    // to whole tree XOR 
    static int counter = 0; 

    // first dfs function to find 
    // xor of subtrees 
    static int dfs_First(int x) 
    { 
        // initializing cal_XOR 
        // array with tree node's value 
        cal_XOR[x] = nodes[x]; 

        // marking each visited node as true 
        visited[x] = true; 

        // iterating through current node children 
        for (int i = 0; i < adj[x].Count; i++) 
        { 
            // check if node->child is not visited 
            if (!visited[adj[x][i]]) 
            { 
                // computing xor of subtree rooted at x 
                cal_XOR[x] ^= dfs_First(adj[x][i]); 
            } 
        } 

        // returning xor of subtree rooted at x 
        return cal_XOR[x]; 
    } 

    // second dfs function to count 
    // subtrees having values equal 
    // to entire XOR of tree nodes values 
    // and returning subtree XOR till that point 
    static int dfs_Second(int x) 
    { 
        // marking each visited node as true 
        visited[x] = true; 

        // storing value of nodes[x] 
        // to another variable temp 
        // for further calculation 
        int temp = nodes[x]; 

        // iterating through current node children 
        for (int i = 0; i < adj[x].Count; i++) 
        { 
            // check if node->child is not visited 
            if (!visited[adj[x][i]]) 
            { 
                // storing xor of subtree 
                temp ^= dfs_Second(adj[x][i]); 
            } 
        } 

        // now, checking if xor of subtree 
        // computed till now is equal to 
        // entire XOR of tree (root) 
        // i.e. value at cal_XOR[0] -> 
        // which will give entire XOR of the tree 
        if (temp == cal_XOR[0]) 
        { 
            // then, make that xor 0 
            temp = 0; 

            // increase count by 1, which 
            // means we found a subtree 
            counter++; 
        } 

        // return xor of subtree 
        // till that point 
        return temp; 
    } 

    // Function to add edges 
    static void addEdge(int u, int v) 
    { 
        adj[u].Add(v); 
        adj[v].Add(u); 
    } 

    // Function to take input 
    // for (n-1) edges 
    static void init(int[,] edges, int numEdges) 
    { 
        for (int i = 0; i < numEdges; i++) 
        { 
            edges[i,0]--; 
            edges[i,1]--; 
            addEdge(edges[i,0], edges[i,1]); 
        } 
    } 

    // Driver Code 
    public static void Main() 
    { 
        // taking input 
        int n = 5, p = 2; 
        nodes[0] = 1; 
        nodes[1] = 6; 
        nodes[2] = 4; 
        nodes[3] = 1; 
        nodes[4] = 2; 

        // making our visited array false 
        for (int i = 0; i < n; i++) 
        { 
            visited[i] = false; 
            adj[i] = new List<int>(); 
        } 

        // taking input for (n-1) edges 
        int[,] edges = new int[4,2] 
                    { { 1, 2 }, { 2, 3 }, { 1, 4 }, { 4, 5 } }; 
        init(edges, 4); 

        // First dfs Function call 
        dfs_First(0); 

        // again, marking visited array to false 
        for (int i = 0; i < n; i++) 
        { 
            visited[i] = false; 
        } 

        // initializing answer variable 
        bool answer = false; 

        // if we found XOR of entire tree 
        // equal to zero, answer = "YES", as 
        // it means there are two connected 
        // components equal to each other 
        // thus, a single edge can be removed 
        if (cal_XOR[0] == 0) 
        { 
            answer = true; 
        } 
        else
        { 
            // second DFS function call 
            dfs_Second(0); 

            // if we found 2 subtree having 
            // equal value with XOR of entire tree 
            // answer is always "YES", such that 
            // p > 2 
            if (counter >= 2 && p != 2) 
            { 
                answer = true; 
            } 
        } 

        // printing the final answer 
        if (answer == true) 
            Console.Write("YES\n"); 
        else
            Console.Write("NO\n"); 

        // making counter = 0 for next iteration 
        counter = 0; 

        for (int i = 0; i < n; i++) 
        { 

            // similarly clearing adjacency list 
            // and both arr and cal_XOR array 
            adj[i].Clear(); 
            cal_XOR[i] = nodes[i] = 0; 
        } 
    } 
  // This code is contributed by Kanishka Gupta
}
JavaScript
 <script>
        // JavaScript code for the above approach
        let N = 1e5;

        // adjacency list to form a tree
        let adj = new Array(N);
        for (let i = 0; i < N; i++) {
            adj[i] = [];
        }

        // array arr defined to store node values
        // array cal_XOR defined to store
        // xor values rooted at i'th level
        let nodes = new Array(N), cal_XOR = new Array(N);

        // defined a visited array
        let visited = new Array(N).fill(false);

        // defined a counter to store
        // number of subtrees having value equal
        // to whole tree XOR
        let counter = 0;

        // first dfs function to find
        // xor of subtrees
        function dfs_First(x)
        {
        
            // initializing cal_XOR
            // array with tree node's value
            cal_XOR[x] = nodes[x];

            // marking each visited node as true
            visited[x] = true;

            // iterating through current node children
            for (let y of adj[x]) 
            {
            
                // check if node->child is not visited
                if (!visited[y]) 
                {
                
                    // computing xor of subtree rooted at x
                    cal_XOR[x] ^= dfs_First(y);
                }
            }

            // returning xor of subtree rooted at x
            return cal_XOR[x];
        }

        // second dfs function to count
        // subtrees having values equal
        // to entire XOR of tree nodes values
        // and returning subtree XOR till that point
        function dfs_Second(x) {

            // marking each visited node as true
            visited[x] = true;

            // storing value of nodes[x]
            // to another variable temp
            // for further calculation
            let temp = nodes[x];

            // iterating through current node children
            for (let y of adj[x]) {

                // check if node->child is not visited
                if (!visited[y]) {

                    // storing xor of subtree
                    temp ^= dfs_Second(y);
                }
            }

            // now, checking if xor of subtree
            // computed till now is equal to
            // entire XOR of tree (root)
            // i.e. value at cal_XOR[0] ->
            // which will give entire XOR of the tree
            if (temp == cal_XOR[0]) {
                // then, make that xor 0
                temp = 0;

                // increase count by 1, which
                // means we found a subtree
                counter++;
            }

            // return xor of subtree
            // till that point
            return temp;
        }
        
        // Function to add edges
        function addEdge(u, v) {
            adj[u].push(v);
            adj[v].push(u);
        }
        
        // Function to take input
        // for (n-1) edges
        function init(edges, numEdges) {
            for (let i = 0; i < numEdges; i++) {
                edges[i][0]--;
                edges[i][1]--;
                addEdge(edges[i][0], edges[i][1]);
            }
        }

        // Driver Code
        // taking input
        let n = 5, p = 2;
        nodes[0] = 1, nodes[1] = 6, nodes[2] = 4,
            nodes[3] = 1, nodes[4] = 2;

        // making our visited array false
        for (let i = 0; i < n; i++) {
            visited[i] = false;
        }

        // taking input for (n-1) edges
        let edges =
            [[1, 2], [2, 3], [1, 4], [4, 5]];
        init(edges, 4);

        // First dfs Function call
        dfs_First(0);

        // again, marking visited array to false
        for (let i = 0; i < n; i++) {
            visited[i] = false;
        }

        // initializing answer variable
        let answer = false;

        // if we found XOR of entire tree
        // equal to zero, answer = "YES", as
        // it means there are two connected
        // components equal to each other
        // thus, a single edge can be removed
        if (cal_XOR[0] == 0) {
            answer = true;
        }
        else {
            // second DFS function call
            dfs_Second(0);

            // if we found 2 subtree having
            // equal value with XOR of entire tree
            // answer is always "YES", such that
            // p > 2
            if (counter >= 2 && p != 2) {
                answer = true;
            }
        }
        
        // printing the final answer
        if (answer == true) {
            document.write("YES" + '<br>')
        }
        else {
            document.write("NO" + '<br>')
        }

        // making counter = 0 for next iteration
        counter = 0;

        for (let i = 0; i < n; i++) {

            // similarly clearing adjacency list
            // and both arr and cal_XOR array
            adj[i] = [];
            cal_XOR[i] = nodes[i] = 0;
        }

        // This code is contributed by Potta Lokesh
    </script>

Output
YES

Time Complexity: O(N+E), where N= number of nodes, and E = number of edges
Auxiliary Space: O(N+E), where N= number of nodes, and E = number of edges

Comment