洛谷 P8491 [IOI2022] 囚徒挑战 题解

IOI 特有的乱搞题。

Step 1(m=52m=52

首先有一个朴素的按位比较的做法,每一个人留下自己当前查看的是 A 还是 B,
查看到第 xx 位的值是多少。如果上一个人查看的是 A,则查看 B 的对应位并比较;否则查看 A 的下一位。

使用二进制,需要 m=13×2×2=52m=13\times 2\times 2 = 52

Step 2(m=39m=39

注意到查看 B 的人并不需要留下这一位 B 的值,只需要告诉下一个人自己查看了 B 的某一位并让下一个人去查看 A 的下一位即可。

需要的 mm 下降到 13×(2+1)=3913\times (2+1)=39

Step 3(m=32m=32

并不一定要是二进制。

改成三进制,mm 下降到 8×(3+1)=328\times (3+1)=32

Step 4(m=24m=24

注意到一个人查看到的是整个数值而不是这个数值的某一位,于是可以交错进行比较:第一个人先查看 A 的第 88 位,并留下「自己看了第 88 位」和「第 88 位 A 的值」两个信息;第二个人看到上述信息后查看 B,如果第 88 位不一样则直接完成比较;否则留下「自己看了第 77 位」和「第 77 位 B 的值」两个信息;第三个人看到上述信息后查看 A,如果第 77 位不一样则直接完成比较;否则留下「自己看了第 66 位」和「第 66 位 A 的值」两个信息,以此类推。

这样的话,直接从位数就能够推断出值是哪个袋子的对应位的值,于是需要的信息量就只有位数 $\times $ 值的种类数。于是我们得到 m=8×3=24m=8\times 3=24

Step 5(m=22m=22

观察一下样例,可以发现:由于保证两个数不同,当我们比较到最后一位的时候,最后一位是 0022 的情况都不需要传回去继续比较。

于是我们需要传出去的情况就减少了两种,得到 m=7×3+1=22m=7\times 3+1=22

Step 6(m=20m=20

考虑充分利用这个顶到两头可以结束比较的性质。

经过一些尝试,可以发现 3×3×3×3×62=5022>50003\times 3\times 3\times 3\times 62=5022>5000

于是我们定义一个数 xx 的第 1,2,3,4,51,2,3,4,5 位 分别是 xmod62,x62mod3,x62×3mod3,x62×32mod3,x62×33mod3x\bmod 62,\lfloor\frac{x}{62}\rfloor\bmod 3,\lfloor\frac{x}{62\times 3}\rfloor\bmod 3,\lfloor\frac{x}{62\times 3^2}\rfloor\bmod 3,\lfloor\frac{x}{62\times 3^3}\rfloor\bmod 3

比较第 5,4,3,25,4,3,2 位的时候和 Step 4 是一样的。我们需要 1212 个状态,分别代表:

  • A 的第 55 位是 0/1/20/1/2
  • B 的第 44 位是 0/1/20/1/2
  • A 的第 33 位是 0/1/20/1/2
  • B 的第 22 位是 0/1/20/1/2

我们最终会查到 A 的第 22 位与 B 的第 22 位相同,并同时获知 A 的第 11 位(记作 A1A_1,B 同理记作 B1B_1)。

此时我们可以利用顶到两头就结束的性质,如下:

  • A1=0A_1=0A1=61A_1=61,则结束比较。
  • 否则按 A1[1,20],A1[21,40],A1[41,60]A_1\in [1,20],A_1\in [21,40],A_1\in [41,60] 分为 33 个状态传给下一个人。

下一个人接收这个状态后查询 B1B_1

  • B1=0B_1=0B1=61B_1=61,则结束比较。
  • B1B_1A1A_1 不处于同一个长度为 2020 的段里面,则结束比较。
  • (B11)mod20=0(B_1-1)\bmod 20=0=19=19,则结束比较。
  • 否则按 (B11)mod20[1,6],(B11)mod20[7,12],(B11)mod20[13,18](B_1-1)\bmod 20\in [1,6],(B_1-1)\bmod 20\in [7,12],(B_1-1)\bmod 20\in [13,18] 分为 33 个状态传给下一个人。

再下一个人接收这个状态后类似地迭代一遍。具体来说,查询 A1A_1

  • (A11)mod20=0(A_1-1)\bmod 20=0=19=19,则结束比较。
  • (A11)mod20(A_1-1)\bmod 20(B11)mod20(B_1-1)\bmod 20 不处于同一个长度为 66 的段里面,则结束比较。
  • ((A11)mod201)mod6=0((A_1-1)\bmod 20-1)\bmod 6=0=5=5,则结束比较。
  • 否则按 ((A11)mod201)mod6[1,2],((A11)mod201)mod6[3,4]((A_1-1)\bmod 20-1)\bmod 6\in [1,2],((A_1-1)\bmod 20-1)\bmod 6\in [3,4] 分为 22 个状态传给下一个人。

再下一个人接收这个状态后查询 B1B_1,由于此时段长为 22,所以一定能够讨论出大小关系。

这样我们就得到了 m=3×4+3+3+2=20m=3\times 4+3+3+2=20,可以通过本题。

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
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
inline vector <int> Construct(int idx, const int &N) {
vector <int> res;
if (idx == 0) {
// Initial
// Check A, return A/62/3/3/3 + 1
res.push_back(0);
for (int i = 1;i <= N;i++) res.push_back(i / 62 / 3 / 3 / 3 + 1);
} else if (idx >= 1 && idx <= 3) {
// A/62/3/3/3 == idx - 1
// Check B, return result if different, otherwise return B/62/3/3 %3 + 4
res.push_back(1);
for (int i = 1;i <= N;i++) {
if (i / 62 / 3 / 3 / 3 > idx - 1) res.push_back(-1);
else if (i / 62 / 3 / 3 / 3 < idx - 1) res.push_back(-2);
else res.push_back(i / 62 / 3 / 3 % 3 + 4);
}
} else if (idx >= 4 && idx <= 6) {
// B/62/3/3 %3 == idx - 4
// Check A, same as above, return A/62/3 %3 + 7
res.push_back(0);
for (int i = 1;i <= N;i++) {
if (i / 62 / 3 / 3 % 3 > idx - 4) res.push_back(-2);
else if (i / 62 / 3 / 3 % 3 < idx - 4) res.push_back(-1);
else res.push_back(i / 62 / 3 % 3 + 7);
}
} else if (idx >= 7 && idx <= 9) {
// A/62/3%3 == idx - 7
// Check B, same as above, return B/62 % 3 + 10
res.push_back(1);
for (int i = 1;i <= N;i++) {
if (i / 62 / 3 % 3 > idx - 7) res.push_back(-1);
else if (i / 62 / 3 % 3 < idx - 7) res.push_back(-2);
else res.push_back(i / 62 % 3 + 10);
}
} else if (idx >= 10 && idx <= 12) {
// B/62%3 == idx - 10
// Check A, return result if different
// if same, return result if A % 62 == 0 or A % 62 == 61
// otherwise return (A % 62 - 1) / 20 + 13
res.push_back(0);
for (int i = 1;i <= N;i++) {
if (i / 62 % 3 > idx - 10) res.push_back(-2);
else if (i / 62 % 3 < idx - 10) res.push_back(-1);
else {
if (i % 62 == 0) res.push_back(-1);
else if (i % 62 == 61) res.push_back(-2);
else res.push_back((i % 62 - 1) / 20 + 13);
}
}
} else if (idx >= 13 && idx <= 15) {
// (A % 62 - 1) / 20 == idx - 13
// Check B, return result if B % 62 == 0 or B % 62 == 61
// otherwise return result if (B % 62 - 1) / 20 != idx - 13
// otherwise return result if (B % 62 - 1) % 20 == 0 / 19
// otherwise return ((B % 62 - 1) % 20 - 1) / 6 + 16
res.push_back(1);
for (int i = 1;i <= N;i++) {
if (i % 62 == 0) res.push_back(-2);
else if (i % 62 == 61) res.push_back(-1);
else {
if ((i % 62 - 1) / 20 < idx - 13) res.push_back(-2);
else if ((i % 62 - 1) / 20 > idx - 13) res.push_back(-1);
else {
if ((i % 62 - 1) % 20 == 0) res.push_back(-2);
else if ((i % 62 - 1) % 20 == 19) res.push_back(-1);
else res.push_back(((i % 62 - 1) % 20 - 1) / 6 + 16);
}
}
}
} else if (idx >= 16 && idx <= 18) {
// ((B % 62 - 1) % 20 - 1) / 6 == idx - 16
// Check A, return result if (A % 62 - 1) % 20 == 0 / 19
// otherwise return result if ((A % 62 - 1) % 20 - 1) / 6 != idx - 16
// otherwise return result if ((A % 62 - 1) % 20 - 1) % 6 == 0 / 5
// otherwise return (((A % 62 - 1) % 20 - 1) % 6 - 1) / 2 + 19
res.push_back(0);
for (int i = 1;i <= N;i++) {
if ((i % 62 - 1) % 20 == 0) res.push_back(-1);
else if ((i % 62 - 1) % 20 == 19) res.push_back(-2);
else {
if (((i % 62 - 1) % 20 - 1) / 6 < idx - 16) res.push_back(-1);
else if (((i % 62 - 1) % 20 - 1) / 6 > idx - 16) res.push_back(-2);
else {
if (((i % 62 - 1) % 20 - 1) % 6 == 0) res.push_back(-1);
else if (((i % 62 - 1) % 20 - 1) % 6 == 5) res.push_back(-2);
else res.push_back((((i % 62 - 1) % 20 - 1) % 6 - 1) / 2 + 19);
}
}
}
} else if (idx >= 19 && idx <= 20) {
res.push_back(1);
for (int i = 1;i <= N;i++) {
if (((i % 62 - 1) % 20 - 1) % 6 == 0) res.push_back(-2);
else if (((i % 62 - 1) % 20 - 1) % 6 == 5) res.push_back(-1);
else {
if ((((i % 62 - 1) % 20 - 1) % 6 - 1) / 2 > idx - 19) res.push_back(-1);
else if ((((i % 62 - 1) % 20 - 1) % 6 - 1) / 2 < idx - 19) res.push_back(-2);
else {
if ((((i % 62 - 1) % 20 - 1) % 6 - 1) % 2 == 0) res.push_back(-2);
else res.push_back(-1);
}
}
}
}
return res;
}

std::vector<std::vector<int> > devise_strategy(int N) {
vector <vector <int> > ans;
for (int i = 0;i <= 20;i++) ans.push_back(Construct(i, N));
return ans;
}