题目:http://codeforces.com/contest/1088/problem/D
题目大意:有0 <= a , b <= 230的a,b两个整数,定义一次操作(c , d)的值为
1 若(a xor c) > (b xor d)则返回1
2 若(a xor c) = (b xor d)则返回0
3 若(a xor c) < (b xor d)则返回-1
现对于a , b两个整数(你不知道它们的值),请你输出62个以内的问题(对于每一个问题,即一组(c , d),它会回答你一个1 、-1、0,含义如上,需要刷新输出),求a , b的值。
看到这道题,首先关注数据范围:a,b 230,输出62个问题,这似乎是二进制每一位输出两个问题啊,那么再思考一下,这两个问题中总有一个是得到a , b这一位上值的吧(不然怎么得到a , b的值)。
我们再看它所定义的操作,它会告诉你对于一组(c,d),(a xor c)与(b xor d)的大小关系,乍一看似乎不知道怎么用,但是至少我们可以得到a , b的大小关系嘛(c = 0 , d = 0)。
那么,我们现在考虑的是,对于已知的a , b大小关系,如何利用上述操作判断a , b每一位上的值。结合上面62个问题,既然每一位两个问题,我们是不是可以前一个问题求出这一位上a , b的大小关系,后一个问题确定这一位上到底是什么呢。那么我们考虑第一个问题,对于每一位i,输出两个(1 << i);
然后我们来分类讨论:(注意:是当前二进制位往后a和b的大小关系(相当于从当前位开始截取))
一 : 从当前位置往后a > b,若输出(1 << i)后
1. a > b,由于我们保证了从当前位置(二进制位)往后a >b,那么,当前位置上,只有3种情况(a = 1 , b = 0; a = 1 , b = 1; a = 0 , b = 0),显然可以发现,只有后两种满足当前情况,那么,我们再输出一个(1 << i)和一个0,就可以判断出是a = b = 0,还是a = b = 1了。
2. a < b,同1,我们可以发现,只有a = 1,b = 0,时满足情况。
3. a不会等于b,因为从当前位置往后有a > b(易证,自己手推一下)。
二:从当前位置往后a == b,这时,我们只需要判断这一位上是a = b = 0,还是a = b = 1就行了。那么,我们输出(1 << i)和一个0,就能够判断了。
三:从当前位置往后a < b,几乎等同于第一种,若输出后:
1. a < b,显然a , b同为0或1,输出(1 << i)和0,即可判断。
2. a > b,只有a = 0,b = 1,满足情况。
3. a不会等于b,情况同上。
那么考虑了当前位置,我们要推进到下一位。
由于我们讨论的是当前位置往后a,b的大小,那么我们到下一位的时候,要考虑当前位对下一位大小的影响。
(下面的a , b都代表a , b二进制当前位置上的值)显然,a = b时,不影响;那么,只有a = 1时,下一次异或时c + (1 << i)即可消除影响,对于b同理,即d + (1 << i)。
总结一下,我们用ansa , ansb记录a , b的大小,nowa,nowb,表示为了消除前面二进制位对大小的影响,c和d应该加上的值,然后每次得到当前位置上a , b的值后,记得推到下一位,确保下一位的大小关系。代码:
#include
typedef long long ll;
using namespace std;
int main()
{
printf("? 0 0\n");//输出0,直接判断a , b的大小,相当于从第30位置开始,做上述操作。
fflush(stdout);
int op;
scanf("%d" , &op);
ll nowa = 0 , nowb = 0;//为了消除前面二进制位对大小的影响,c和d应该加上的值
ll ansa = 0 , ansb = 0;//记录a , b的答案
for (int i = 29; i >= 0; i--)
{
ll now = (1 << i);
if (op == 0)
{
cout << "? "<< nowa + now << " " << nowb << endl;//直接判断这一位上a,b的值
fflush(stdout);
int op2;
scanf("%d" , &op2);
if (op2 == -1)
{
ansa += now;
ansb += now;//a = b = 1
}
continue;
}
cout << "? "<< nowa + now << " " << nowb + now << endl;//判断这一位上a , b的大小
fflush(stdout);
int op2;
scanf("%d" , &op2);
if (op == 1)
{
if (op2 == 1)
{
cout << "? "<< nowa << " " << nowb + now << endl;
//输出nowa + now和nowb效果一样,可以手推一下试试
fflush(stdout);
int op3;
scanf("%d" , &op3);
if (op3 == 1)
{
ansa += now;
ansb += now;
}
//a,b同为0或1,推进到下一位时大小不变
}
else
{
ansa += now;//这一位上只有a为1
nowa += now;//为保证之后位的大小不受这一位的影响,下次异或时加上这一位的值
cout << "? "<< nowa << " " << nowb << endl;
fflush(stdout);
scanf("%d" , &op);//推进到下一位后的大小
continue;
}
}
else
{
if (op2 == -1)
{
cout << "? "<< nowa << " " << nowb + now << endl;
fflush(stdout);
int op3;
scanf("%d" , &op3);
if (op3 == 1)
{
ansa += now;
ansb += now;
}
//a,b同为0或1,推进到下一位时大小不变
}
else
{
ansb += now;//这一位上只有b为1
nowb += now;//为保证之后位的大小不受这一位的影响,下次异或时加上这一位的值
cout << "? "<< nowa << " " << nowb << endl;
fflush(stdout);
scanf("%d" , &op);//推进到下一位后的大小
continue;
}
}
}
cout << "! "<< ansa << " " << ansb << endl;
}