CodeForces-835E The penguin's game(思维+二进制乱搞+交互题)

题意:

给出n个数,其中有两个特殊的数值为y,其他不特殊的数值为x,现要求你与评测机交互进行不超过19次询问找出两个特殊值得位置,询问是:你给出该数列的一个任意子集,评测机返回该子集内所有数的异或值。

思路:

贼6的二进制思维题,也是头一回做的交互题。自己想不出来,看的大佬的博客

由于保证x和y值不同且都不为0,所以每次询问的子集异或值只会有四种取值(x, y, x^y, 0),然后观察发现当该异或值为y或者x^y时,表明该子集中一定只存在一个y。又,因为值为y的两个位置pos1,pos2肯定不同,那么pos1和pos2的二进制形式一定至少存在一位它们俩是不同的,那么我们就根据二进制位确定子集,即从二进制低位到高位每次都选取该位为1的位置组成一个集合进行询问,因为n <= 1000,那么我们最多花费9次(如果前9次都不可行,第10次必定能行)就能将所有数分为两个集合,使每个集合都有且仅有一个y。那么我们再至多通过9次询问不断缩小其中一个集合就能确定出一个y的位置。然而再怎么求第二个位置呢?显然只剩一次询问是无法确定的了,其实我们在前9次询问中寻找pos1和pos2位二进制位不同的时候,通过一个bit来记录两个二进制位是否相同,相同则令bit当前位为0,不同当前位为1(所以我们在前面询问时不管分没分成两个集合都要询问完所有位了,所以最多达到10次)。最后求出ans1,与bit异或一下就是ans2了。


代码:

#include <bits/stdc++.h>
using namespace std;
int n, x, y, _xor, bit;
set<int> s1, s2;
set<int>::iterator it;
vector<int> vt1, vt2;
string str;
string chg(int x)
{
	string res;
	while(x) res += x%10+'0', x /= 10;
	reverse(res.begin(), res.end());
	return " " + res;
}
void find(int pos)
{
	for(int i = 0; i <= 9; ++i)
	{
		if(i == pos) continue;
		str = ""; vt1.clear(), vt2.clear();
		for(it = s2.begin(); it != s2.end(); ++it)
		if((*it)&(1<<i)) str += chg(*it), vt1.push_back(*it);
		else vt2.push_back(*it);
		if(vt1.empty()) continue;
		cout << ("?"+chg(vt1.size())+str) << endl;
	//	fflush(stdout);
		cin >> _xor;
		if(_xor == (x^y) || _xor == y)
		{
			for(int j = 0; j < vt2.size(); ++j)
			s2.erase(vt2[j]);
		}
		else
		{
			for(int j = 0; j < vt1.size(); ++j)
			s2.erase(vt1[j]);
		}
	}
	int ans1 = *(s2.begin()), ans2 = 0;
	ans2 = ans1^bit;
	if(ans1 > ans2) swap(ans1, ans2);
	cout << "! " << ans1 << " " << ans2 << endl;
}
void work()
{
	int pos = -1; bit = 0;
	for(int i = 0; i <= 9; ++i)
	{
		str = ""; s1.clear();
		for(int j = 1; j <= n; ++j)
		if(j&(1<<i)) str += chg(j), s1.insert(j);
		if(s1.empty()) continue;
		cout << ("?"+chg(s1.size())+str) << endl;
	//	fflush(stdout);
		cin >> _xor;
		if(_xor == (x^y) || _xor == y)
		{
			bit += 1<<i;
			if(s2.empty()) s2 = s1, pos = i;
		}
	}
	find(pos);
}
int main()
{
	ios::sync_with_stdio(0);
	cin >> n >> x >> y;
	work();
	return 0;
}


继续加油~

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值