CF2219B2 Unique Values (Hard version) 题解
思路
下文中钦定 \(x\) 为出现了三次的那个数。
首先,我们会发现:如果序列中每一个数都只出现了两次,则一个前缀长度的奇偶性和这个前缀的答案的奇偶性相等。因为每次加入一个数时,答案只会加一(第一次出现)或者减一(第二次出现)。
查询次数 \(33\) 次,想到 \(\log\) 的做法。考虑二分。我们会发现,如果当前查询到一个前缀,满足这个前缀长度的奇偶性和这个前缀的答案的奇偶性不相等,就说明在这个前缀中,一定出现了三次 \(x\)。二分就可以得到最后一个 \(x\) 的位置。考虑继续二分。这次从最后一个 \(x\) 的位置向前二分。查询时加上最后一个 \(x\) 的位置,就可以得到第二个 \(x\) 的位置。同理可得最前面 \(x\) 的位置。
查询次数 \(3 \cdot \log(2n + 1) \le 33\),可以通过此题。
代码
#include <bits/stdc++.h>
using namespace std;
int t, n, resx, resy, resz;
int query(int x, int numx, int numy)
{
vector<int> num;
for (int i = 1; i <= x; i++)
num.push_back(i);
if (numx != -1) num.push_back(numx);
if (numy != -1) num.push_back(numy);
printf("? %d ", (int)num.size());
for (int i : num) printf("%d ", i);
printf("\n");
fflush(stdout);
int v;
scanf("%d", &v);
return v;
}
void solve()
{
scanf("%d", &n);
int l = 1, r = 2 * n + 1;
resx = resy = resz = 0;
while (l <= r)
{
int mid = (l + r) >> 1;
if ((query(mid, -1, -1) & 1) ^ (mid & 1)) resx = mid, r = mid - 1;
else l = mid + 1;
}
l = 1, r = resx;
while (l <= r)
{
int mid = (l + r) >> 1;
if ((query(mid, resx, -1) & 1) ^ ((mid + 1) & 1)) resy = mid, r = mid - 1; // 这里 mid + 1 是因为多了一个数
else l = mid + 1;
}
l = 1, r = resy;
while (l <= r)
{
int mid = (l + r) >> 1;
if ((query(mid, resx, resy) & 1) ^ (mid & 1)) resz = mid, r = mid - 1; // 这里不用加是因为多了两个数,奇偶性不变
else l = mid + 1;
}
printf("! %d %d %d\n", resz, resy, resx);
fflush(stdout);
}
int main()
{
scanf("%d", &t);
while (t--) solve();
return 0;
}

浙公网安备 33010602011771号