CF2222E Seek the Truth

· · 题解

upd on 2026.4.28:解释了操作次数。

所以还是交互题好玩。

题意

太长了,自己去看题。

解法

首先容易注意到题目中保证 c\not=0,所以我们可以直接令 a=0,并将其作为 S 的初始元素。

现在来看如何找到答案。 ::::info[k=1]{open}

我们可以这样构造 $c$:对于 $0\le i<n$,每次插入 $2^i$,如果说在插入后 $|S|$ 变了,就说明 $c$ 的第 $i+1$ 个二进制位是 $1$,那么我们就把 $2^i$ 加入答案。 :::: ::::info[$k=2$ 或 $3$]{open} 因为你在第一次插入了 $0$,而 $0 \operatorname{or} c=0\operatorname{xor} c=c$,所以此时 $S=\{0,c\}$。那么我们可以通过第二种查询,二分找到 $c$。 由于异或的性质,我们可以找到 $c$ 在二进制下的某一位 $1$(假设它是第 $i$ 位),然后向 $S$ 插入 $2^i$。如果说 $|S|$ 没变,那么就说明是按位或运算($k=2$),因为此时 $c\operatorname{or} 2^i=c$。否则 $k=3$,因为此时 $c$ 的第 $i$ 位上的 $1$ 会因为异或变成 $0$ 导致多了一个数。 :::: 代码实现就是按照上面的过程模拟。 对于与运算,用了 $n+1$ 次 `I` 操作,所以操作数是 $n+1$;对于剩下两种运算,二分中最多用 $n$ 次 `Q` 操作,若 $c$ 不是 $2$ 的幂,则仅用 $1$ 次 `I` 操作,若 $c$ 是 $2$ 的幂,则要用 $1$ 次 `I` 操作和 $1$ 次 `Q` 操作,总操作数最多 $1+n+1+1=n+3$ 次。 ::::success[Code] ```cpp int n; inline void init (i64 x) { cout << x << endl; return ; } inline i64 I (i64 x) { cout << "I " << x << endl; i64 res; cin >> res; return res; } inline i64 Q (i64 x) { cout << "Q " << x << endl; i64 res; cin >> res; return res; } inline void A (int k, i64 c) { cout << "A " << k << " " << c << endl; return ; } inline i64 find () { i64 l = 1, r = (1ll << n) - 1, ans = r; while (l <= r) { i64 mid = l + r >> 1; if (Q (mid) > 0) ans = mid, l = mid + 1; else r = mid - 1; } return ans; }//二分 inline void solve () { cin >> n; int k; i64 c = 0; init (0); i64 siz = I (0); if (siz == 1) {//按位与运算 k = 1; for (i64 i = 0; i < n; ++ i) { i64 now = I (1ll << i); if (now == siz + 1) c |= (1ll << i); siz = now; } A (k, c); return ; } else { c = find (); //这里为了方便取了最低位的 1 if ((c & -c) == c) { I ((1ll << n) - 1); i64 cnt = Q ((1ll << n) - 1); if (cnt == 1) k = 2; else k = 3; //特判 c 是不是 2 的幂,因为这样的话不管 k 是多少插入后集合大小都不会变 } else { i64 now = I(c & -c); if (now == 3) k = 3;//按位异或运算 else k = 2;//按位或运算 } A (k, c); return ; } } ``` ::::