cf1415 E1. Bitwise Queries (Easy Version)

题意:

交互题

猜一个数组,长度已知为 n

每次可询问 \(i,j\) 并选择 and/or/xor 中的一种运算,回答 \(a_i\) 运算 \(a_j\) 的值

n 是 2 的倍数,\(0\le a_i < n-1\)

询问次数:Easy Version \(n+2\),Hard Version \(n+1\)

思路:

key:\(a+b=a\oplus b + 2(a\&b)\) (到底怎么发现的啊)

异或老好用了,知道了一个数的值,其他数都跟它异或一下就能得到所有数。

那怎样算出一个数呢?

easy ver

询问 \(a_1 \& a_2,a_2\&a_3,a_1\&a_3\),然后问 \(a_1\oplus a_2,a_1\oplus a_3\),那么 \(a_2\oplus a_3=(a_1\oplus a_2)\oplus (a_1\oplus a_3)\)

代进那个神奇等式,就知道了 \(a_1+a_2,a_2+a_3,a_1+a_3\),解方程算出 \(a_1,a_2,a_3\)。也就是说我们用 5 个询问得到了三个数。接下来问 n-3 个异或即可。

hard ver

先把所有数和 1 异或。这样就花掉 n-1 次询问了,接下来要用两次询问求出 \(a_1\)

如果某个数 x 异或 1 得 0,说明 \(x=a_1\)。直接询问 \(a_1=a_1 | x\)

如果某俩数 \(x\oplus a_1=y\oplus a_1\),说明 \(x=y\)。直接询问 \(x=x|y\)

否则,说明数组中的数两两不同,即 \([0,n-1]\) 中的每个数出现一次。那么肯定有一个与 \(a_1\) “二进制互补” 的数 x,\(a_1\oplus x=(1111)_2=n-1\)

显然 \(a_1\& x=0\),我们再随便另取一个 y,询问 \(x\&y\)。这样又可以解方程得到三个数

//easy version
const signed N = (1<<17);
const char op[3][5] = {"AND","OR","XOR"};
int ask(int i, int j, int ty) {
    cout << op[ty] << ' ' << i << ' ' << j << endl;
    int res; cin >> res; return res;
}

int n, a[N];
signed main() {
    cin >> n;
    for(int i = 2; i <= n; i++) a[i] = ask(1,i,2);
    int a12 = ask(1,2,0), a23 = ask(2,3,0), a13 = ask(1,3,0);
    a12 = a12*2+a[2], a13 = a13*2+a[3], a23 = a23*2+(a[2]^a[3]);
    a[1] = (a12+a13+a23)/2-a23;
    cout << "! " << a[1];
    for(int i = 2; i <= n; i++) cout << ' ' << (a[i]^a[1]);
    cout << endl;
}
posted @ 2022-04-27 11:01  Bellala  阅读(39)  评论(0)    收藏  举报