CF1746E 题解
题意
这是一道交互题。
目标:求出整数 。
每次询问一个集合 ,交互库会返回是否 。交互库会骗你,但不会连续两次骗你。
其中,,至多询问 次。交互库自适应
题解
我们可以把条件转化为怎么确定一个数不是 :连续两次的回答不符。这里“不符”指的是,交互库返回 YES 但 不在询问的集合中,或者交互库返回 NO 但 在询问的集合中。
先不考虑如何询问,只考虑如何最优化交互次数。可以设计一个 dp:
我们发现所有可能的数只有两种状态:连续 次与回答不符和连续 次与回答不符。因为如果连续 次,那么这个数就不可能是我们要求的答案
设 表示当前有 个数连续 次与回答不符,有 个数连续 次与回答不符,至少要问几次,则:
相当于:对于每次询问,从这 个数中挑出 个数,从 个数中挑出 个数,组成本次的询问集合。
- 若交互库返回 NO,则剩下的 个数数都是符合本次回答的,而挑出来的这 个数都是不符的,因此这 个数变成了连续一次不符, 个数连续两次不符被丢掉,剩下的 个数就都变成了连续 次与回答不符,因此对应
- 若交互库返回 YES,则这 个数是符合的,剩下的 个数不符合,那么 个数都变成了连续 次与回答不符, 个变成连续 次不符, 个因为连续两次不符被丢掉,因此对应
- 由于交互库是自适应的,它会挑使你交互次数尽可能大的那个答案,因此要从两种结果中取 。
- 由于 和 是可以自己挑的,所以肯定尽可能调使答案最小的那个结果。
下面我们来考虑具体怎么询问
- 我们把 展开,维护两个集合 , 集合代表连续 次不符的数组成的集合, 集合代表连续 次不符的数组成的集合
- 从前面 数组中找到最优的 , 然后从 中随便选出 个数组成集合 , 中剩下的数组成集合 , 从 中选出 个数组成集合 , 中剩下的数组成集合 , 然后向交互库询问
- 根据交互库返回的结果,按照上面的 dp 的转移方式得到新的集合 ,以此类推
考虑优化,这个 dp 的复杂度显然是 ,直接爆算肯定不行,那么我们可以在 比较大的时候估计一个较优的 ,小范围(例如 )的时候再直接 dp。至于怎么估计,当然是人类智慧,我们可以发现均分 和 是比较优的。
一些细节见代码:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
using namespace std;
typedef long long ll;
int n;
int dp[21][21], xm[21][21], ym[21][21];
vector<int> a, b;
int main()
{
ios::sync_with_stdio(false);
memset(dp, 0x3f, sizeof(dp));
for (int l = 0; l <= 20; l++)
{
for (int i = 0; i <= l; i++)
{
int j = l - i;
if (l <= 2)
dp[i][j] = 0;
for (int x = 0; x <= i; x++)
{
for (int y = 0; y <= j; y++)
{
int tmp = max(dp[i + j - x - y][x], dp[x + y][i - x]) + 1;
if (tmp < dp[i][j])
dp[i][j] = tmp, xm[i][j] = x, ym[i][j] = y;
}
}
}
}
cin >> n;
for (int i = 1; i <= n; i++)
a.push_back(i);
while (a.size() + b.size() > 2)
{
int x, y;
if (a.size() + b.size() <= 20)
x = xm[a.size()][b.size()], y = ym[a.size()][b.size()];
else
x = a.size() / 2, y = b.size() / 2;
vector<int> a1(a.begin(), a.begin() + x), a2(a.begin() + x, a.end());
vector<int> b1(b.begin(), b.begin() + y), b2(b.begin() + y, b.end());
a.clear(), b.clear();
cout << "? " << a1.size() + b1.size() << " ";
for (int i : a1)
cout << i << " ";
for (int i : b1)
cout << i << " ";
cout << endl;
string in;
cin >> in;
if (in == "YES")
{
for (int i : a1)
a.push_back(i);
for (int i : b1)
a.push_back(i);
for (int i : a2)
b.push_back(i);
}
else
{
for (int i : a2)
a.push_back(i);
for (int i : b2)
a.push_back(i);
for (int i : a1)
b.push_back(i);
}
}
for (int i : a)
{
cout << "! " << i << endl;
string in;
cin >> in;
if (in == ":)")
return 0;
}
for (int i : b)
{
cout << "! " << i << endl;
string in;
cin >> in;
if (in == ":)")
return 0;
}
return 0;
}