XOR Queries on a given set
Given a set S with the initial element 0 that is S = { 0 }. The task is to perform each query when Q number of queries are given and print the answer after every query of type 3.
We can perform three types of query operations:
- 1 X: We can add X to the set S.
- 2 Y: For each element i, perform i ? Y.
- 3: Find the minimum element of the set.
Examples:
Input: Q = 10,
3
1 7
3
2 4
2 8
2 3
1 10
1 3
3
2 1
Output: 0 0 3
Explanation:
For the given 10 queries, the changes in the set for each query is as follows:
- The minimum is 0.
- The number 7 added to S –> {0, 7}.
- The minimum is still 0.
- All of the numbers in S are changed to their xor with 4 –> {4, 3}.
- All of the numbers in S are changed to their xor with 8 –> {12, 11}.
- All of the numbers in S are changed to their xor with 3 –> {15, 8}.
- The number 10 added to S –> {15, 8 ,10}.
- The number 3 added to S –> {15, 8, 10, 3}.
- The minimum is now 3.
- All of the numbers in S are changed to their xor with 1 –> {14, 9, 11, 2}.
Input: Q = 6
3
1 7
3
1 4
2 8
3
Output: 0 0 8
Prerequisite: Trie.
Approach:
We will try to solve this problem by using the trie approach of Minimum XOR Value Pair Problem.
- So, In this problem, we have a binary trie and an integer x, we have to find the minimum value of XOR(x, y) where y is some integer from the trie.
- Now, To solve this problem we will go down the trie from the most significant bit to the least.
- Suppose we are at ith bit:
If x[i] is 1, we will go down the path of the trie which has 1.
If x[i] is 0, we will go down the path which has 0.
If at position i, we do not have a branch to go down x[i], we will go the other way.
Now, coming to our problem.
- Suppose we have inserted a1, a2, a3 in the set and then xor everything with x1, x2, x3, then it is same as XOR-ing with X = XOR(x1, x2, x3).
- So, finding the minimum element is equivalent to finding the minimum among (a1, a2, a3) after XOR-ing with X.
We have already noticed how to do that in the beginning.
- Now, How to answer each of the queries.
Let x = XOR(x1, x2, ….., xn), where x1, x2, …, xn are all numbers asked to XOR with the element of the set S.
- For Query 2, XOR with xi.
we will not XOR every element in the trie with xi. instead, we will just update x = XOR(x, xi)
- For Query 3, getting the minimum element.
We were supposed to have XOR-ed the entire array so far with X.
So, now we will just calculate the minimum value obtained by taking XOR of all the elements in the set with X by using the above-mentioned approach.
- For Query 1, insert ai.
We will not insert ai into the trie, but XOR(ai, x). Reason:
- Suppose we Insert a1, a2, then XOR with x1, then insert a3, then XOR with x2.
- When we query for minimum now, we will find the minimum value in : {XOR(a1, x1, x2), XOR(a2, x1, x2), XOR(a3, x1, x2)}
But a3 has only been XOR-ed with x2 and not x1.
- So it is critical that at every moment in time that we insert an element ai into the trie, we insert XOR(ai, x). This ensures that when we calculate the minimum, it will cancel out the previous XORs.
So, in our example, our trie will contain
{a1, a2, XOR(a3, x1)}.
- When we query the minimum value of XOR(x), we will be finding the minimum using above method of {XOR(a1, x1, x2), XOR(a2, x1, x2), XOR(a3, x2)}, which is what we want. Inserting XOR(ai, x) will ensure that whatever we do the minimum operation, we do not do any unnecessary XORs on any ai.
Below are the implementations of this approach:
CPP
#include <bits/stdc++.h>
using namespace std;
const int Maxbits = 30;
void Insert( int x, int curx,
int * sz, int trie[100][2])
{
x = x ^ curx;
int p = 0;
for ( int i = Maxbits - 1; i >= 0; i--)
{
if (!trie[p][x >> i & 1])
trie[p][x >> i & 1] = (*sz)++;
p = trie[p][x >> i & 1];
}
}
void XorQuery( int x, int * curx)
{
(*curx) = (*curx) ^ x;
}
void MinXor( int x, int trie[100][2])
{
int ans = 0, p = 0;
for ( int i = Maxbits - 1; i >= 0; i--)
{
bool Currbit = (x >> i & 1);
if (trie[p][Currbit])
p = trie[p][Currbit];
else {
p = trie[p][!Currbit];
ans |= 1 << i;
}
}
cout << ans << endl;
}
int main()
{
int sz = 1;
int curx = 0;
int trie[100][2] = { 0 };
Insert(0, 0, &sz, trie);
MinXor(curx, trie);
Insert(7, curx, &sz, trie);
MinXor(curx, trie);
XorQuery(4, &curx);
XorQuery(8, &curx);
XorQuery(3, &curx);
Insert(10, curx, &sz, trie);
Insert(3, curx, &sz, trie);
MinXor(curx, trie);
XorQuery(1, &curx);
return 0;
}
|
Java
import java.util.Scanner;
class GFG{
static final int Maxbits = 30 ;
static int sz = 1 ;
static int curx = 0 ;
static int [][] trie = new int [ 100 ][ 2 ];
static void Insert( int x, int curx) {
x = x ^ curx;
int p = 0 ;
for ( int i = Maxbits - 1 ; i >= 0 ; i--) {
if (trie[p][x >> i & 1 ] == 0 )
trie[p][x >> i & 1 ] = sz++;
p = trie[p][x >> i & 1 ];
}
}
static void XorQuery( int x) {
curx = curx ^ x;
}
static void MinXor( int x) {
int ans = 0 , p = 0 ;
for ( int i = Maxbits - 1 ; i >= 0 ; i--) {
boolean Currbit = (x >> i & 1 ) == 1 ;
if (trie[p][Currbit ? 1 : 0 ] != 0 )
p = trie[p][Currbit ? 1 : 0 ];
else {
p = trie[p][Currbit ? 0 : 1 ];
ans |= 1 << i;
}
}
System.out.println(ans);
}
public static void main(String[] args) {
Insert( 0 , 0 );
MinXor(curx);
Insert( 7 , curx);
MinXor(curx);
XorQuery( 4 );
XorQuery( 8 );
XorQuery( 3 );
Insert( 10 , curx);
Insert( 3 , curx);
MinXor(curx);
XorQuery( 1 );
}
}
|
Python3
Maxbits = 30
def Insert(x, curx, trie):
global sz
x = x ^ curx
p = 0
for i in range (Maxbits - 1 , - 1 , - 1 ):
if not (trie[p][x >> i & 1 ]):
trie[p][x >> i & 1 ] = sz
sz + = 1
p = trie[p][x >> i & 1 ]
def XorQuery(x,):
global curx
curx ^ = x
def MinXor(x, trie):
ans = 0
p = 0
for i in range (Maxbits - 1 , - 1 , - 1 ):
Currbit = (x >> i & 1 )
if (trie[p][Currbit]):
p = trie[p][Currbit]
else :
p = trie[p][~Currbit]
ans | = 1 << i
print (ans)
if __name__ = = '__main__' :
sz = 1
curx = 0
trie = [[ 0 ] * 2 for _ in range ( 100 )]
Insert( 0 , 0 , trie)
MinXor(curx, trie)
Insert( 7 , curx, trie)
MinXor(curx, trie)
XorQuery( 4 , )
XorQuery( 8 , )
XorQuery( 3 , )
Insert( 10 , curx, trie)
Insert( 3 , curx, trie)
MinXor(curx, trie)
XorQuery( 1 ,)
|
C#
using System;
class GFG{
static int Maxbits = 30;
static int sz = 1;
static int curx = 0;
static int [,] trie = new int [100,2];
static void Insert( int x, int curx)
{
x = x ^ curx;
int p = 0;
for ( int i = Maxbits - 1; i >= 0; i--) {
if (trie[p,x >> i & 1] == 0)
trie[p,x >> i & 1] = sz++;
p = trie[p,x >> i & 1];
}
}
static void XorQuery( int x) {
curx = curx ^ x;
}
static void MinXor( int x) {
int ans = 0, p = 0;
for ( int i = Maxbits - 1; i >= 0; i--) {
bool Currbit = (x >> i & 1) == 1;
if (trie[p,Currbit ? 1 : 0] != 0)
p = trie[p,Currbit ? 1 : 0];
else {
p = trie[p,Currbit ? 0 : 1];
ans |= 1 << i;
}
}
Console.WriteLine(ans);
}
public static void Main() {
Insert(0, 0);
MinXor(curx);
Insert(7, curx);
MinXor(curx);
XorQuery(4);
XorQuery(8);
XorQuery(3);
Insert(10, curx);
Insert(3, curx);
MinXor(curx);
XorQuery(1);
}
}
|
Javascript
<script>
const Maxbits = 30;
let sz = 1;
let curx = 0;
const trie = new Array(100).fill().map(() => new Array(2).fill(0));
function Insert(x, curx) {
x = x ^ curx;
let p = 0;
for (let i = Maxbits - 1; i >= 0; i--) {
if (trie[p][(x >> i) & 1] == 0)
trie[p][(x >> i) & 1] = sz++;
p = trie[p][(x >> i) & 1];
}
}
function XorQuery(x) {
curx = curx ^ x;
}
function MinXor(x) {
let ans = 0, p = 0;
for (let i = Maxbits - 1; i >= 0; i--) {
let Currbit = (x >> i) & 1;
if (trie[p][Currbit ? 1 : 0] != 0)
p = trie[p][Currbit ? 1 : 0];
else {
p = trie[p][Currbit ? 0 : 1];
ans |= 1 << i;
}
}
document.write(ans);
}
Insert(0, 0);
MinXor(curx);
Insert(7, curx);
MinXor(curx);
XorQuery(4);
XorQuery(8);
XorQuery(3);
Insert(10, curx);
Insert(3, curx);
MinXor(curx);
XorQuery(1);
</script>
|
Time Complexity: O(N) per query. Where N is the number of bits in the query element.
Auxiliary Space: O(N)
Last Updated :
26 Feb, 2023
Like Article
Save Article
Share your thoughts in the comments
Please Login to comment...