题意:
给出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;
}
继续加油~