Codeforces Round #685 (Div. 2) E2 - Bitwise Queries (Hard Version)题解

E Bitwise Queries

题意

交互题,有一个 n n n个数( n n n 2 2 2的幂次)的序列,每个数范围 [ 0 , n − 1 ] [0,n-1] [0,n1]。最多给出 n + 1 n+1 n+1次询问,能得到任意两个数的AND或OR或XOR。根据询问还原序列。

想法

easy version的比较简单,直接 n − 1 n-1 n1次的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 n1次的XOR(都与第一个数XOR)链接每个数。然后可以发现最多 n − 1 n-1 n1个数,分两种情况

  • 有重复的数
    • 和第一个数相同, a 1 X O R a j = 0 , 2 ≤ j ≤ n a_1 XOR a_j=0,2 \leq j\leq n a1XORaj=0,2jn。直接用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),2i,jn。那么找到这些位置,用 a i A N D a j a_i AND a_j aiANDaj来赋值。同样得到全部数。
  • 没有重复的数,那么这n个数分布在 [ 0 , n − 1 ] [0,n-1] [0,n1]。根据异或的性质,第一个数与后面 n − 1 n-1 n1个数的异或结果范围为 [ 1 , n − 1 ] [1,n-1] [1,n1]。那么找到异或结果为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;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值