Appraiser
按 个每块分组,共分了 组,还余 个之后处理。每个组内询问相邻的点对,这样一个组需要询问 次,共 次。
由于只有 个坏点,那么若一组 个全部相等则说明这 个都是好的。我们记下已知的任意一个好点。否则,这一组内至少有一个坏点,留下备用。最多只有 个坏点,因此最多只有 个可疑的组。
我们知道了一组内两两间的关系,因此只需要知道组内任意一个点的属性就可以确认整组的。我们拿那个已知的好点去和每个可疑组的任意一点做询问,这样可以求出所有可疑组内所有点的情况。这样的询问最多需要 次。
另外,还单独余下 个点。挨个和好点做比较即可。需要 次。
最后,总共 次。
#include <bits/extc++.h>
using namespace std;
namespace pbds = __gnu_pbds;
istream& fin = cin;
ostream& fout = cout;
using ui = unsigned int;
using uli = unsigned long long int;
using li = long long int;
bool ask(size_t x, size_t y) {
fout << "? " << x + 1 << ' ' << y + 1 << endl;
bool res;
fin >> res;
return res;
}
int main(void) {
ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
size_t n, m, q;
fin >> n >> m >> q;
size_t target = ~0;
vector<vector<bool>> d;
vector<size_t> special;
for (ui i = 0; i + m + 1 <= n; i += m + 1) {
d.emplace_back();
for (ui j = 0; j < m; ++j) d.back().emplace_back(ask(i + j, i + j + 1));
if (count(d.back().begin(), d.back().end(), true))
special.emplace_back(d.size() - 1);
else
target = i;
}
vector<size_t> ans;
for (size_t i : special) {
bool lst = ask(i * (m + 1), target);
if (lst) ans.emplace_back(i * (m + 1));
for (size_t j = 0; j < m; ++j)
if (lst ^= d[i][j]) ans.emplace_back(i * (m + 1) + j + 1);
}
for (size_t i = n - n % (m + 1); i < n; ++i)
if (ask(target, i)) ans.emplace_back(i);
assert(ans.size() == m);
fout << "! ";
for (size_t i : ans) fout << i + 1 << ' ';
return 0;
}