Library of Magic
本题两种情况分类讨论。设有 。
当 时, 到 的所有数都是 。我们可以二分出第一个 使得 , 即为 。询问次数为 。
当 时,直接二分就不好使了。不过仔细分析一些例子可以发现:一定可以找到一个 ,满足 且 。 最大不超过 ,直接枚举即可。
证明:当答案的某位是 时, 中有零个或两个当前位为 。考虑 的最高位 ,其为 ,那么 或 中有且只有一个第 位也为 。为了保证 ,我们选择 的第 位为 ,而 的第 位为 。这时,便有 。
找到 后, 和 一定在 中,在该区间内用类似的方法二分出 ,则最后一个数就一定在 中,直接问一次就好了。
使用 C++20,希望参选本题最优美代码奖。
#include <algorithm>
#include <iostream>
#include <ranges>
using namespace std;
istream& fin = cin;
ostream& fout = cout;
using ui = unsigned int;
using uli = unsigned long long int;
using li = long long int;
uli ask(uli l, uli r) {
fout << "xor " << l << ' ' << r << endl;
uli ans;
fin >> ans;
return ans;
}
int main(void) {
ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
size_t T;
fin >> T;
while (T--) {
uli n;
fin >> n;
uli x = ask(1, n) == 0
? [n] {
for (uli i = 2; i < n; i *= 2) {
uli res = ask(1, i - 1);
if (res != 0) return res;
}
exit(300);
}()
: [n] {
auto view = ranges::views::iota(1ull, n + 1) |
ranges::views::transform(
[](uli v) -> bool { return ask(1, v); });
auto it = ranges::lower_bound(view, true);
return uli(it - view.begin()) + 1ull;
}();
uli y = [n, x] {
auto view = ranges::views::iota(x + 1, n + 1) |
ranges::views::transform(
[x](uli v) -> bool { return ask(x + 1, v); });
auto it = ranges::lower_bound(view, true);
return uli(it - view.begin()) + (x + 1);
}();
uli z = ask(y + 1, n);
fout << "ans " << x << ' ' << y << ' ' << z << endl;
}
return 0;
}