Open In App

XOR of a submatrix queries

Given an N * N matrix and q queries, each containing position of top-left and bottom-right corner of a rectangular sub-matrix, the task is to find the xor of all the elements from this sub-matrix.
Examples: 
 

Input: arr[][] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}, q[] = {{1, 1, 2, 2}, {1, 2, 2, 2}} 
Output: 

15 
Query 1: 5 ^ 6 ^ 8 ^ 9 = 2 
Query 2: 6 ^ 9 = 15
Input: arr[][] = {{21, 2}, { 14, 5}}, q[] = {{0, 1, 1, 1}, {0, 0, 1, 1}} 
Output: 

28 
 



 

A simple solution is to find the XOR of the entire sub-matrix for each query. Thus, the worst case time complexity for each query will be O(n2).
Efficient Approach: We are all familiar with the idea of prefix XOR on linear array i.e. 
 



if arr[] = {1, 2, 3, 4} and we calculate prefixXOR[] = {1, 3, 0, 4} where prefixXOR[i] stores the XOR of values from arr[0] to arr[i] 
Then the XOR of sub-array arr[i] to arr[j] can be found as prefixXOR[j] ^ prefixXOR[i – 1] 
For example, the XOR of sub-array {2, 3} will be XOR(0, 1) = 1 
This is because in the prefixXOR values for {1, 2, 3} and {1}, the value 1 got repeated twice which will give 0 as the XOR result and will not affect the value of the other XOR operations. 
 

We will try to extend the same to the 2-D matrix. We will compute a prefix-XOR matrix which will help us in solving each query in O(1). 
In this case, our prefix-XOR matrix at position (R, C) will store the XOR of the rectangular sub-matrix with top-left corner at(0, 0) and bottom-right corner at (R, C). 
We calculate the prefix-XOR in two steps. 
 

  1. Calculate the prefix-XOR for each row of the original matrix from left to right.
  2. On the above matrix, calculate the prefix XOR for each column from top to bottom.

Once, we have the required prefix XOR-matrix, we can answer the queries with simplicity. XOR of a sub-matrix from (R1, C1) to (R2, C2) can be computed as prefix_xor[R2][C2] ^ prefix_xor[R1 – 1][C2] ^ prefix_xor[R2][C1 – 1] ^ prefix_xor[R1 – 1][C1 – 1].
Note: If R1 or C1 equals 0, then R1 – 1 or C1 – 1 should also be 0.
Below is the implementation of the above approach: 
 




// C++ implementation of the approach
#include <iostream>
#define n 3
using namespace std;
 
// Function to pre-compute the xor
void preComputeXor(int arr[][n], int prefix_xor[][n])
{
    // Left to right prefix xor
    // for each row
    for (int i = 0; i < n; i++)
        for (int j = 0; j < n; j++) {
            if (j == 0)
                prefix_xor[i][j] = arr[i][j];
            else
                prefix_xor[i][j]
                    = (prefix_xor[i][j - 1] ^ arr[i][j]);
        }
 
    // Top to bottom prefix xor
    // for each column
    for (int i = 0; i < n; i++)
        for (int j = 1; j < n; j++)
            prefix_xor[j][i]
                = (prefix_xor[j - 1][i] ^ prefix_xor[j][i]);
}
 
// Function to process the queries
// x1, x2, y1, y2 represent the
// positions of the top-left
// and bottom right corners
int ansQuerie(int prefix_xor[][n], int x1, int y1, int x2, int y2)
{
 
    // To store the xor values
    int xor_1 = 0, xor_2 = 0, xor_3 = 0;
 
    // Finding the values we need to xor
    // with value at (x2, y2) in prefix-xor
    // matrix
    if (x1 != 0)
        xor_1 = prefix_xor[x1 - 1][y2];
    if (y1 != 0)
        xor_2 = prefix_xor[x2][y1 - 1];
    if (x1 != 0 and y1 != 0)
        xor_3 = prefix_xor[x1 - 1][y1 - 1];
 
    // Return the required prefix xor
    return ((prefix_xor[x2][y2] ^ xor_1) ^ (xor_2 ^ xor_3));
}
 
// Driver code
int main()
{
    int arr[][n] = { { 1, 2, 3 },
                     { 4, 5, 6 },
                     { 7, 8, 9 } };
 
    // To store pre-computed xor
    int prefix_xor[n][n];
 
    // Pre-computing xor
    preComputeXor(arr, prefix_xor);
 
    // Queries
    cout << ansQuerie(prefix_xor, 1, 1, 2, 2) << endl;
    cout << ansQuerie(prefix_xor, 1, 2, 2, 2) << endl;
    return 0;
}




// Java implementation of the approach
class GfG
{
     
static int n = 3;
 
// Function to pre-compute the xor
static void preComputeXor(int arr[][],
                            int prefix_xor[][])
{
    // Left to right prefix xor
    // for each row
    for (int i = 0; i < n; i++)
        for (int j = 0; j < n; j++)
        {
            if (j == 0)
                prefix_xor[i][j] = arr[i][j];
            else
                prefix_xor[i][j] =
                    (prefix_xor[i][j - 1] ^ arr[i][j]);
        }
 
    // Top to bottom prefix xor
    // for each column
    for (int i = 0; i < n; i++)
        for (int j = 1; j < n; j++)
            prefix_xor[j][i] =
                (prefix_xor[j - 1][i] ^ prefix_xor[j][i]);
}
 
// Function to process the queries
// x1, x2, y1, y2 represent the
// positions of the top-left
// and bottom right corners
static int ansQuerie(int prefix_xor[][], int x1,
                    int y1, int x2, int y2)
{
 
    // To store the xor values
    int xor_1 = 0, xor_2 = 0, xor_3 = 0;
 
    // Finding the values we need to xor
    // with value at (x2, y2) in prefix-xor
    // matrix
    if (x1 != 0)
        xor_1 = prefix_xor[x1 - 1][y2];
    if (y1 != 0)
        xor_2 = prefix_xor[x2][y1 - 1];
    if (x1 != 0 && y1 != 0)
        xor_3 = prefix_xor[x1 - 1][y1 - 1];
 
    // Return the required prefix xor
    return ((prefix_xor[x2][y2] ^ xor_1) ^ (xor_2 ^ xor_3));
}
 
// Driver code
public static void main(String[] args)
{
    int arr[][] = new int[][]{{ 1, 2, 3 },
                            { 4, 5, 6 },
                            { 7, 8, 9 }};
 
    // To store pre-computed xor
    int prefix_xor[][] = new int[n][n];
 
    // Pre-computing xor
    preComputeXor(arr, prefix_xor);
 
    // Queries
    System.out.println(ansQuerie(prefix_xor, 1, 1, 2, 2));
    System.out.println(ansQuerie(prefix_xor, 1, 2, 2, 2));
}
}
 
// This code is contributed by
// Prerna Saini.




n = 3
 
# Function to pre-compute the xor
def preComputeXor(arr, prefix_xor):
     
    # Left to right prefix xor
    # for each row
    for i in range(n):
        for j in range(n):
            if (j == 0):
                prefix_xor[i][j] = arr[i][j]
            else:
                prefix_xor[i][j] = (prefix_xor[i][j - 1] ^
                                               arr[i][j])
 
    # Top to bottom prefix xor
    # for each column
    for i in range(n):
        for j in range(1, n):
            prefix_xor[j][i] = (prefix_xor[j - 1][i] ^
                                    prefix_xor[j][i])
 
# Function to process the queries
# x1, x2, y1, y2 represent the
# positions of the top-left
# and bottom right corners
def ansQuerie(prefix_xor, x1, y1, x2, y2):
 
    # To store the xor values
    xor_1, xor_2, xor_3 = 0, 0, 0
 
    # Finding the values we need to xor
    # with value at (x2, y2) in prefix-xor
    # matrix
    if (x1 != 0):
        xor_1 = prefix_xor[x1 - 1][y2]
    if (y1 != 0):
        xor_2 = prefix_xor[x2][y1 - 1]
    if (x1 != 0 and y1 != 0):
        xor_3 = prefix_xor[x1 - 1][y1 - 1]
 
    # Return the required prefix xor
    return ((prefix_xor[x2][y2] ^ xor_1) ^
                         (xor_2 ^ xor_3))
 
 
# Driver code
arr = [[ 1, 2, 3 ],
       [ 4, 5, 6 ],
       [ 7, 8, 9 ]]
 
# To store pre-computed xor
prefix_xor = [[0 for i in range(n)]
                 for i in range(n)]
 
# Pre-computing xor
preComputeXor(arr, prefix_xor)
 
# Queries
print(ansQuerie(prefix_xor, 1, 1, 2, 2))
print(ansQuerie(prefix_xor, 1, 2, 2, 2))
 
# This code is contributed by Mohit Kumar




// C# implementation of the approach
using System;
 
class GfG
{
    static int n = 3;
     
    // Function to pre-compute the xor
    static void preComputeXor(int [,]arr,
                                int [,]prefix_xor)
    {
        // Left to right prefix xor
        // for each row
        for (int i = 0; i < n; i++)
            for (int j = 0; j < n; j++)
            {
                if (j == 0)
                    prefix_xor[i, j] = arr[i, j];
                else
                    prefix_xor[i, j] =
                   (prefix_xor[i, j - 1] ^ arr[i, j]);
            }
     
        // Top to bottom prefix xor
        // for each column
        for (int i = 0; i < n; i++)
            for (int j = 1; j < n; j++)
                prefix_xor[j, i] =
                    (prefix_xor[j - 1, i] ^
                     prefix_xor[j, i]);
    }
     
    // Function to process the queries
    // x1, x2, y1, y2 represent the
    // positions of the top-left
    // and bottom right corners
    static int ansQuerie(int [,]prefix_xor, int x1,
                         int y1, int x2, int y2)
    {
     
        // To store the xor values
        int xor_1 = 0, xor_2 = 0, xor_3 = 0;
     
        // Finding the values we need to xor
        // with value at (x2, y2) in prefix-xor
        // matrix
        if (x1 != 0)
            xor_1 = prefix_xor[x1 - 1, y2];
        if (y1 != 0)
            xor_2 = prefix_xor[x2, y1 - 1];
        if (x1 != 0 && y1 != 0)
            xor_3 = prefix_xor[x1 - 1, y1 - 1];
     
        // Return the required prefix xor
        return ((prefix_xor[x2,y2] ^ xor_1) ^
                            (xor_2 ^ xor_3));
    }
     
    // Driver code
    public static void Main()
    {
        int [,]arr = {{ 1, 2, 3 },
                      { 4, 5, 6 },
                      { 7, 8, 9 }};
     
        // To store pre-computed xor
        int [,]prefix_xor = new int[n, n];
     
        // Pre-computing xor
        preComputeXor(arr, prefix_xor);
     
        // Queries
        Console.WriteLine(ansQuerie(prefix_xor, 1, 1, 2, 2));
        Console.WriteLine(ansQuerie(prefix_xor, 1, 2, 2, 2));
    }
}
 
// This code is contributed by Ryuga




<?php
// PHP implementation of the approach
 
$n = 3;
 
// Function to pre-compute the xor
function preComputeXor($arr, &$prefix_xor)
{
    global $n;
     
    // Left to right prefix xor
    // for each row
    for ($i = 0; $i < $n; $i++)
        for ($j = 0; $j < $n; $j++)
        {
            if ($j == 0)
                $prefix_xor[$i][$j] = $arr[$i][$j];
            else
                $prefix_xor[$i][$j] = ($prefix_xor[$i][$j - 1] ^
                                              $arr[$i][$j]);
        }
 
    // Top to bottom prefix xor
    // for each column
    for ($i = 0; $i < $n; $i++)
        for ($j = 1; $j < $n; $j++)
            $prefix_xor[$j][$i] = ($prefix_xor[$j - 1][$i] ^
                                   $prefix_xor[$j][$i]);
}
 
// Function to process the queries
// x1, x2, y1, y2 represent the
// positions of the top-left
// and bottom right corners
function ansQuerie($prefix_xor, $x1,
                   $y1, $x2, $y2)
{
 
    // To store the xor values
    $xor_1 = $xor_2 = $xor_3 = 0;
 
    // Finding the values we need to xor
    // with value at (x2, y2) in prefix-xor
    // matrix
    if ($x1 != 0)
        $xor_1 = $prefix_xor[$x1 - 1][$y2];
    if ($y1 != 0)
        $xor_2 = $prefix_xor[$x2][$y1 - 1];
    if ($x1 != 0 and $y1 != 0)
        $xor_3 = $prefix_xor[$x1 - 1][$y1 - 1];
 
    // Return the required prefix xor
    return (($prefix_xor[$x2][$y2] ^ $xor_1) ^
                           ($xor_2 ^ $xor_3));
}
 
// Driver code
$arr = array(array( 1, 2, 3 ),
             array( 4, 5, 6 ),
             array( 7, 8, 9 ));
 
// To store pre-computed xor
$prefix_xor = array_fill(0, $n,
              array_fill(0, $n, 0));
 
// Pre-computing xor
preComputeXor($arr, $prefix_xor);
 
// Queries
echo ansQuerie($prefix_xor, 1, 1, 2, 2) . "\n";
echo ansQuerie($prefix_xor, 1, 2, 2, 2);
 
// This code is contributed by mits
?>




<script>
 
// Javascript implementation of the approach
var n = 3;
 
    // Function to pre-compute the xor
    function preComputeXor(arr , prefix_xor)
    {
        // Left to right prefix xor
        // for each row
        for (i = 0; i < n; i++)
            for (j = 0; j < n; j++) {
                if (j == 0)
                    prefix_xor[i][j] = arr[i][j];
                else
                    prefix_xor[i][j] =
                    (prefix_xor[i][j - 1] ^ arr[i][j]);
            }
 
        // Top to bottom prefix xor
        // for each column
        for (i = 0; i < n; i++)
            for (j = 1; j < n; j++)
                prefix_xor[j][i] =
                (prefix_xor[j - 1][i] ^ prefix_xor[j][i]);
    }
 
    // Function to process the queries
    // x1, x2, y1, y2 represent the
    // positions of the top-left
    // and bottom right corners
    function ansQuerie(prefix_xor , x1 , y1 , x2 , y2) {
 
        // To store the xor values
        var xor_1 = 0, xor_2 = 0, xor_3 = 0;
 
        // Finding the values we need to xor
        // with value at (x2, y2) in prefix-xor
        // matrix
        if (x1 != 0)
            xor_1 = prefix_xor[x1 - 1][y2];
        if (y1 != 0)
            xor_2 = prefix_xor[x2][y1 - 1];
        if (x1 != 0 && y1 != 0)
            xor_3 = prefix_xor[x1 - 1][y1 - 1];
 
        // Return the required prefix xor
        return ((prefix_xor[x2][y2] ^ xor_1) ^
                               (xor_2 ^ xor_3));
    }
 
    // Driver code
     
        var arr = [[ 1, 2, 3 ], [ 4, 5, 6 ],
                                [ 7, 8, 9 ] ];
 
        // To store pre-computed xor
        var prefix_xor = Array(n);
        for(var i = 0;i<n;i++)
        prefix_xor[i] = Array(n).fill(0);
 
        // Pre-computing xor
        preComputeXor(arr, prefix_xor);
 
        // Queries
        document.write(ansQuerie(prefix_xor, 1, 1, 2, 2)+
        "<br/>");
        document.write(ansQuerie(prefix_xor, 1, 2, 2, 2));
 
// This code contributed by umadevi9616
 
</script>

Output: 
2
15

 

Time Complexity: O(n2)

Auxiliary Space: O(n2), since n2 extra space has been taken.


Article Tags :