Codeforces Round #525 (Div. 2) D Ehab and another another xor problem

题目: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;
}

你可能感兴趣的:(Codeforces Round #525 (Div. 2) D Ehab and another another xor problem)