题解:CF2078E Finding OR Sum
题意
这是一道交互题。
SPJ 有两个你初始未知的数 \(x,y\),你可以进行最多两次的询问,每次询问,你向标准输出给出一行一个整数 \(n\),SPJ 从标准输入返回一行一个值代表 \((n \mathbin{|} x) + (n \mathbin{|} y)\)。其中 \(|\) 代表按位或。在进行完至多两次的询问后,你需要向标准输出给出一行一个字符 !,然后 SPJ 会从标准输入返回一行一个值 \(m\),你需要输出 \((m \mathbin{|} x) + (m \mathbin{|} y)\)。有多测。
题解
我们看到两次询问,每次询问都得获得一半的信息。我们考虑第一次询问的值是 0x2AAAAAAA。转换成二进制就是 0b101010...10。我们可以从中获得 \(x\) 和 \(y\) 的第 \(0,2,4...\) 位的信息。由于 0b101010...10 不管按位或 \(x\) 还是 \(y\),或完的结果都是 0b1?1?1?...1?,第 \(1,3,5...\) 位的值都是 \(1\),我们可以减掉 0b101010...10,以获得裸的 \(x,y\) 的偶数位上的信息。而因为返回的答案是 \((n \mathbin{|} x) + (n \mathbin{|} y)\),所以需要减两次。减完的就是只保留了第 \(0,2,4...\) 位的 \(x,y\) 相加。
我们把减完的答案设为 \(a_1\),考虑到如果 \(x,y\) 在该位上的值可能都是 \(1\),从而导致进位,所以我们考虑把在二进制下的 \(a_1\) 两位两位分成一组,第 \(0,1\) 位是一组,第 \(2,3\) 位是一组。我们考虑一组里的两位。很显然只会有三种情况:00,01,10。那么三种情况分别对应 \(x,y\) 在该位都是 \(1\),有一个是 \(1\),两个都是 \(1\)。第二次询问 \(n\) 是 0x15555555,如法炮制,我们就可以获得第 \(1,3,5...\) 位的信息。这样我们就可以求出 \(x,y\) 的具体值。
那有人要问了:有一个是 \(1\) 怎么搞?我又不知道那个是 \(1\)。其实无所谓。我们最终要求的 \((m \mathbin{|} x) + (m \mathbin{|} y)\)。考虑 \(m\) 这位是 \(0\)。那么显然,无论这个 \(1\) 给 \(x\) 或是 \(y\),\(x \mathbin{|} m\) 和 \(y \mathbin{|} m\) 在那一位上的和也还是 \(1\)。对于 \(m\) 那一位是 \(1\),那就更无所谓了,因为无论是啥或上 \(1\) 都是 \(1\)。
代码
#include<bits/extc++.h>
#define int long long
using namespace std;
const int q1 = 0x2aaaaaaa,q2 = 0x15555555;
int a1,a2,x,y,m;
void solve()
{
cout << q1 << endl;
cin >> a1;
a1 -= q1 * 2;// 减去询问的 n,得到裸的信息
cout << q2 << endl;
cin >> a2;
a2 -= q2 * 2;
x = y = 0;// 记得清空
for (int i = 0; i < 30; i++)
{
int tmp = i & 1 ? a2 : a1;
// 对于奇数位,我们获得的信息是 a1,偶数位就是 a2
if (tmp & (1 << i))// 如果是 01 的情况
x |= (1 << i);// 只有一个 1
else if (tmp & (1 << (i + 1)))// 00 或者 10 时,判断是 00 还是 10
{// 如果是 10 就是 x,y 都是 1,反之都是 0
x |= (1 << i);
y |= (1 << i);
}
}
cout << '!' << endl;
cin >> m;
cout << (m | x) + (m | y) << endl;
}
signed main()
{
int t;
cin >> t;
while (t--)
solve();
return 0;
}
本文来自博客园,使用 CC BY-NC-SA 4.0 协议。
作者:伊埃斯,转载请注明原文链接:https://www.cnblogs.com/Eous/articles/18765078

浙公网安备 33010602011771号