PROBLEM LINK:
Practice
Div-2 Contest
Div-1 Contest
Video Editorial
Author and Editorialist: Kritagya Agarwal
Tester: Suchan Park
DIFFICULTY:
Easy - Medium
PREREQUISITES:
Bitwise Xor, Linear Equations
PROBLEM:
Given an array A of size N.
We need to find the value of A[1]⊕A[2]⊕.....⊕A[N] , where ⊕ represents Bitwise Xor Operation by asking at most 20 queries.
In eack query we can ask the value of A[1]⊕K + A[2]⊕K + ..... + A[N]⊕K, (1 \le K \le 2*10^6)
QUICK EXPLANATION:
- In i^{th} query, choose K = 2^{i-1} for all 1 \le i \le 20
- Form linear equations using queries with variables as x_k (0 \le k \le 19), representing the number of elements in array with k^{th} bit ON.
- Find the parity or value of x_k by solving linear equations. If the parity of x_k is Odd, then answer will have k^{th} bit ON.
EXPLANATION:
Since A[i] \le10^6, there can be at most 20 bits in binary representation of A[i] for all 1 \le i \le N.
Lets, define x_k for 0 \le k \le 19, as the number of elements in array A with k^{th} bit ON.
If we know the values of x_k, then if parity of x_k is Odd, answer will have k^{th} bit ON. This holds due to the property of xor operator.
To find the value of x_k :-
In i^{th} query, choose K = 2^{i-1}. Suppose answer to given query is q_i. Using the property of xor operator, we can claim that :-
- 2^{0} \cdot x_0 + 2^{1} \cdot x_1 + ..... + 2^{i} \cdot (N - x_i)+ ....... + 2^{19} \cdot x_{19} = q_i
as all the elements with i^{th} bit OFF will contribute to q_i due to property of Xor.
Above equation can be rewritten as :-
- 2^{0} \cdot x_0 + 2^{1} \cdot x_1 + ..... - 2^{i} \cdot x_i + ....... + 2^{19} \cdot x_{19} = q_i - N \cdot 2^{i}
Thus, using 20 queries, we get 20 linear equations with variables as x_k. We can solve these linear equations to get the value of x_k.
Any system of Linear Equations A \cdot x = b can be represented as :
So, we have given values of A, x, b here :
Note : det(A) != 0, hence there exists a unique solution to system of Linear Equations.
In Python, linear equation can be solved using linalg Function .
Note : Don’t forget to use around function to get integer values of x_k.
ALTERNATE EXPLANATION 1:
Thanks to Navneel for this approach.
Another possible method (without explicitly carrying out Gaussian Elimination) is to use the following:
Suppose the query is 1 2^k for k \in \{0, 1, \dots, 19\}. Then if the number of elements whose i^\mathrm{th}
bit is on is x_k , then the difference between the answer of the query (say v_k ) and the sum of all numbers would be (n - 2 \cdot x_k).2^k
Note that the sum of all numbers is in fact \sum_{k = 0}^{19} x_k.2^k so we have :-
v_k - n.2^k = x_0.2^0 + ... + x_{k-1}.2^{k-1} - x_k.2^k + ... + x_n.2^n
Treating this as a set of linear equations in x_i's, we write it as
Note that the inverse of the 20 \cdot 20 matrix i.e. A is in fact :
So our final results if x = A'.b. We can get the parity of x_k from this. If x_k is Odd, then k^{th} bit is ON in answer.
ALTERNATE EXPLANATION 2
Thanks to Divyansh for this approach.
SOLUTIONS:
Setter's Solution (Python)
import numpy as np
def ask_query(k):
print(1, k)
ans = int(input())
if ans == -1:
exit()
return ans
a = np.array([
[-1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,32768,65536,131072,262144,524288],
[1,-2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,32768,65536,131072,262144,524288],
[1,2,-4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,32768,65536,131072,262144,524288],
[1,2,4,-8,16,32,64,128,256,512,1024,2048,4096,8192,16384,32768,65536,131072,262144,524288],
[1,2,4,8,-16,32,64,128,256,512,1024,2048,4096,8192,16384,32768,65536,131072,262144,524288],
[1,2,4,8,16,-32,64,128,256,512,1024,2048,4096,8192,16384,32768,65536,131072,262144,524288],
[1,2,4,8,16,32,-64,128,256,512,1024,2048,4096,8192,16384,32768,65536,131072,262144,524288],
[1,2,4,8,16,32,64,-128,256,512,1024,2048,4096,8192,16384,32768,65536,131072,262144,524288],
[1,2,4,8,16,32,64,128,-256,512,1024,2048,4096,8192,16384,32768,65536,131072,262144,524288],
[1,2,4,8,16,32,64,128,256,-512,1024,2048,4096,8192,16384,32768,65536,131072,262144,524288],
[1,2,4,8,16,32,64,128,256,512,-1024,2048,4096,8192,16384,32768,65536,131072,262144,524288],
[1,2,4,8,16,32,64,128,256,512,1024,-2048,4096,8192,16384,32768,65536,131072,262144,524288],
[1,2,4,8,16,32,64,128,256,512,1024,2048,-4096,8192,16384,32768,65536,131072,262144,524288],
[1,2,4,8,16,32,64,128,256,512,1024,2048,4096,-8192,16384,32768,65536,131072,262144,524288],
[1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,-16384,32768,65536,131072,262144,524288],
[1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,-32768,65536,131072,262144,524288],
[1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,32768,-65536,131072,262144,524288],
[1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,32768,65536,-131072,262144,524288],
[1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,32768,65536,131072,-262144,524288],
[1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,32768,65536,131072,262144,-524288]
])
for _ in range(int(input())):
x = 1
b = [None]*20
n = int(input())
for i in range(0,20):
b[i] = ask_query(x)-n*x
x *= 2
b = np.array(b)
c = np.linalg.solve(a, b)
c = np.around(c)
ans = 0
x = 1
for i in range(0,20):
if(c[i]%2 != 0):
ans += x;
x *= 2
print(2,ans)
if int(input()) == -1:
exit()
Tester Solution (Kotlin)
import kotlin.system.exitProcess
class FINXOR_SOLVER(val ask: (Int) -> Int) {
fun solve(N: Int): Int {
val sum = ask(1 shl 20) - (1 shl 20) * N
var answer = sum % 2
for (b in 1 until 20) {
val diff = ask(1 shl b) - sum
require(diff and ((1 shl b) - 1) == 0)
val num_ones = (N - (diff shr b)) / 2
if (num_ones % 2 == 1) {
answer = answer xor (1 shl b)
}
}
return answer
}
}
fun main(args: Array<String>) {
val T = readLine()!!.toInt()
require(T in 1..100)
repeat(T) {
val solver = FINXOR_SOLVER { k: Int ->
println("1 $k")
val ret = readLine()!!.toInt()
if (ret == -1) {
exitProcess(0)
}
ret
}
val N = readLine()!!.toInt()
val answer = solver.solve(N)
println("2 $answer")
val response = readLine()!!.toInt()
if (response == -1) {
exitProcess(0)
}
}
}
Alternative Approach (C++)
#include <bits/stdc++.h>
using namespace std;
#define int long long
int32_t main() {
int t;
cin >> t;
int m[20][20] = {0};
m[19][19] = -17;
for (int i = 0; i < 19; ++i) {
m[19][i] = 1;
}
for (int i = 18; ~i; --i) {
for (int j = 0; j < 20; ++j) {
m[i][j] = m[i + 1][(j + 1) % 20] * 2;
}
}
int div = 18 << 20;
while (t--) {
int n;
cin >> n;
int s;
vector<int> v(20);
for (int i = 19; ~i; --i) {
cout << 1 << " " << (1 << i) << endl;
cin >> s;
s -= (n << i);
v[i] = s;
}
int ans = 0;
for (int i = 0; i < 20; ++i) {
int cur = 0;
for (int j = 0; j < 20; ++j) {
cur += m[i][j] * v[j];
}
cur /= div;
ans += (cur & 1) << i;
}
cout << 2 << " " << ans << endl;
cin >> s;
assert(s == 1);
}
return 0;
}