E Bitwise Queries
题意
交互题,有一个 n n n个数( n n n为 2 2 2的幂次)的序列,每个数范围 [ 0 , n − 1 ] [0,n-1] [0,n−1]。最多给出 n + 1 n+1 n+1次询问,能得到任意两个数的AND或OR或XOR。根据询问还原序列。
想法
easy version的比较简单,直接 n − 1 n-1 n−1次的XOR来链接这些数然后用3次的两两OR来确定前三个数(能根据XOR结果知道前三个数哪些位两两相同并且用OR来确定这些相同的位的值,XOR为 0 0 0的位OR为 0 0 0则全 0 0 0,OR为 1 1 1则全 1 1 1)。
hard version需要用到每个数的范围和power of two这个条件,同样不可缺少的是 n − 1 n-1 n−1次的XOR(都与第一个数XOR)链接每个数。然后可以发现最多 n − 1 n-1 n−1个数,分两种情况
- 有重复的数
- 和第一个数相同, a 1 X O R a j = 0 , 2 ≤ j ≤ n a_1 XOR a_j=0,2 \leq j\leq n a1XORaj=0,2≤j≤n。直接用AND来给第一个数赋值, a 1 = a 1 A N D a j a_1=a_1 AND a_j a1=a1ANDaj。
- 除了第一个数的两个数相同 a i X O R a j = 0 ( a i X O R a 1 = a j X O R a 1 ) , 2 ≤ i , j ≤ n a_i XOR a_j=0(a_i XOR a_1 = a_j XOR a_1),2 \leq i,j\leq n aiXORaj=0(aiXORa1=ajXORa1),2≤i,j≤n。那么找到这些位置,用 a i A N D a j a_i AND a_j aiANDaj来赋值。同样得到全部数。
- 没有重复的数,那么这n个数分布在 [ 0 , n − 1 ] [0,n-1] [0,n−1]。根据异或的性质,第一个数与后面 n − 1 n-1 n−1个数的异或结果范围为 [ 1 , n − 1 ] [1,n-1] [1,n−1]。那么找到异或结果为1和2的两个位置, a 1 X O R a i = 1 , a 1 X O R a j = 2 a_1 XOR a_i = 1,a_1 XOR a_j = 2 a1XORai=1,a1XORaj=2。分别来确定 a 1 a_1 a1的除了 2 0 2^0 20和 2 1 2^1 21位的全部值,从而确定 a 1 a_1 a1。
代码
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <iostream>
#include <map>
#include <vector>
using namespace std;
const int maxn = 1e5 + 5;
typedef long long ll;
int a[1 << 17];
int q1(int i, int j) {
printf("OR %d %d\n", i, j);
fflush(stdout);
int x;
scanf("%d", &x);
return x;
}
int q2(int i, int j) {
printf("AND %d %d\n", i, j);
fflush(stdout);
int x;
scanf("%d", &x);
return x;
}
int q3(int i, int j) {
printf("XOR %d %d\n", i, j);
fflush(stdout);
int x;
scanf("%d", &x);
return x;
}
int res[1 << 17];
int n;
inline void cal() {
for (int i = 2; i <= n; ++i) {
a[i] = a[1] ^ res[i];
}
}
map<int, int> mp;
int main() {
scanf("%d", &n);
int flag1 = 0, flag2 = 0;
int p1, p2;
for (int i = 2; i <= n; ++i) {
res[i] = q3(1, i);
}
for (int i = 2; i <= n; ++i) {
if (res[i] == 0) {
flag1 = i;
break;
}
if (res[i] == 1) p1 = i;
if (res[i] == 2) p2 = i;
if (mp.find(res[i]) != mp.end()) {
flag2 = 1;
p1 = i, p2 = mp[res[i]];
break;
}
mp[res[i]] = i;
}
if (flag1) {
a[1] = q2(1, flag1);
} else if (flag2) {
a[p1] = q2(p1, p2);
a[1] = a[p1] ^ res[p1];
} else {
a[1] = q2(1, p1);
int tp = q2(1, p2);
if (a[1] & 1) a[1] ^= 1;
a[1] ^= (tp & 1);
}
cal();
printf("!");
for (int i = 1; i <= n; ++i) printf(" %d", a[i]);
puts("");
return 0;
}