CF1624D – Finding Zero – 题解与思路重现

给大家表演一个一场下青

CodeForces Round 770 Div.2 Problem D – Finding Zero

一道非常有意思的交互和构造题。

考虑能否固定两个数$x, y$,通过它们的相对大小关系,确定其他数中是否存在$0$或最大值$a_{\text{max}}$。

经过一番推导,我们发现,设$x \geq y$的情况下,对于三元组$(a_i=x, a_j=y, a_k=z)$,有$$g(i, j, k)\newline
=\max(a_i, a_j, a_k) – \min(a_i, a_j, a_k)=
\begin{cases}z-y, & z > x \\
x-y, & z \in [y, x]\\
x-z, & z < y\end{cases}$$。所以,设$x=6, y=4$以上式结果为因变量,$z$为自变量,应有以下图像:

容易发现,在给定的$a$序列当中取$z$,当$g$取到最大值时,$z$为$0$或者$a_{\text{max}}$。令$x=\max(a_1, a_2), y=\min(a_1, a_2)$。现在我们就已经知道了数列中其中一个最值的位置。那么,如何判别这是$a_{\text{max}}$还是$0$呢?

设该最值的位置为$p$。容易发现,不论$a_p$是$a_{\text{max}}$还是$0$,设$q$为另一个最值的位置,则有$\max(a_i, a_j, a_p) – \min(a_i, a_j, a_p), p \neq i, p\neq j$在$j=q$或$i=q$时取到最大值,否则与题设和定义相矛盾。同时,由于题目要求输出至多两个可能存在$0$的位置,我们需要更准确地判定另一个最值的位置。则我们枚举$i$,令$j=i \mod n+1$,计算$g(i, j, p)$,则由于$g(q-1, q, p)$和$g(q, q+1, p)$两个结果均会取到最大值,故我们可以由此确定$q$的位置。这时答案就是$(p, q)$。

还有一个比较棘手的问题:当$\forall i, j, 3 \leq i < j \leq n, g(1, 2, i)=g(1, 2, j)$怎么办?一开始(赛时)我采取分类讨论的想法,发现出现这种情况,只可能是因为

  1. $x, y$中既包含$a_{\text{max}}$又包含$0$(下图粉红色点集);
  2. $x, y$中包含$0$,且$\forall i \in [3, n], a_i=a_{\text{max}}$(下图绿色点集);
  3. $x, y$中不包含$0$,但$\forall i \in [3, n], (a_i>x \wedge a_i-y=\text{constant})\vee(a_i<y \wedge x-a_i=\text{constant})$(下图浅蓝色点集)。

 

为什么不包含“$x$是$a_{\text{max}}$,$y\neq 0$”的情况?因为这样一来,由题意可得$a_3=a_4=\dots=a_n=0$,与题设相矛盾。

再来考虑有什么办法能够将这三种情况统一起来。最开始,我考虑选出$x, y, z, c$,分别计算$g(x, y, c), g(x, z, c), g(y, z, c)$,然后取$g$的最小值,认为其补集就是最值所在位置(例如,若$g(x, z, c)$取到最小,则$y$是最值)。但这种方法在第三种情况会出问题。

但今天我发现,这三种情况有一个共性:最大值和最小值紧挨在一起,即$|p-q|=1$或$|p-q|=n-1$。第一种情况无需证明;对于情况二,无论$a_1=0$还是$a_2=0$,都有$a_n=a_{\text{max}}=a_3$;第三种情况,容易证明$\forall i \in [3, n], a_i = a_{\text{max}} \vee a_i=0$。所以,我们直接计算所有三元组$g(i, i \mod n + 1, (i + 1)\mod n+1), i \in [1, n]$,找到$g$的最大值,和上述一种情况一样选择产生$g_p=g_{p+1}=g_{\text{max}}$的$p$,计算$p$和$p+1$代表的三元组的交集即可。

总体来看,对于情况A,我们使用了$(n-2)+(n-1)=2n-3$次询问;情况B则采用了恰好$2n-2$次询问。

实现时应当注意各种细节。Submission #145491065

  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
  8. 8
  9. 9
  10. 10
  11. 11
  12. 12
  13. 13
  14. 14
  15. 15
  16. 16
  17. 17
  18. 18
  19. 19
  20. 20
  21. 21
  22. 22
  23. 23
  24. 24
  25. 25
  26. 26
  27. 27
  28. 28
  29. 29
  30. 30
  31. 31
  32. 32
  33. 33
  34. 34
  35. 35
  36. 36
  37. 37
  38. 38
  39. 39
  40. 40
  41. 41
  42. 42
  43. 43
  44. 44
  45. 45
  46. 46
  47. 47
  48. 48
  49. 49
  50. 50
  51. 51
  52. 52
  53. 53
  54. 54
  55. 55
  56. 56
  57. 57
  58. 58
  59. 59
  60. 60
  61. 61
  62. 62
  63. 63
  64. 64
  65. 65
  66. 66
  67. 67
  68. 68
  69. 69
  70. 70
  71. 71
  72. 72
  73. 73
  74. 74
  75. 75
  76. 76
  77. 77
  78. 78
  79. 79
  80. 80
  81. 81
  82. 82
  83. 83
  84. 84
  85. 85
  86. 86
  87. 87
  88. 88
  89. 89
  90. 90
  91. 91
  92. 92
  93. 93
  94. 94
  95. 95
  96. 96
  97. 97
  98. 98
  99. 99
  100. 100
  101. 101
  102. 102
#include <bits/stdc++.h> using namespace std; template <typename T> inline bool read (T &x) { x = 0; int f = 1; char c = getchar (); for (; c != EOF && !isdigit (c); c = getchar ()) if (c == '-') f = -1; if (c == EOF) return 0; for (; c != EOF && isdigit (c); c = getchar ()) x = (x<<1) + (x<<3) + (c^48); x *= f; return 1; } template <typename T, typename... Targs> inline bool read (T &x, Targs&... args) { return read (x) && read (args...); } template <typename T> void print (T x) { if (x < 0) putchar ('-'), x = -x; if (x > 9) print (x / 10); putchar ('0' + x % 10); } template <typename T, typename... Targs> inline void print (T x, Targs... args) { print (x), putchar (' '), print (args...); } #define reint register int #define inl inline #define newl putchar('\n') typedef long long ll; typedef unsigned long long ull; // typedef __int128 lll; typedef pair <int, int> pint; constexpr int N = 1024; int T, n, keko[N], pos[N][2], mx, mxfr; bool f, out; int tot; inl int publ (int a[2], int b[2]) { int g[4] = { a[0], a[1], b[0], b[1] }; sort (g, g + 4); for (int i = 1; i < 4; ++i) if (g[i] == g[i - 1]) return g[i]; } inl int ask (int i, int j, int k) { printf ("? %d %d %d\n", i, j, k); fflush (stdout); int res; scanf ("%d", &res); return fflush (stdout), res; } #define outp(x, y) (printf ("! %d %d\n", x, y), fflush (stdout)) #define proc(x) ((((x) - 1) + n) % n + 1) int main () { /* CF1634D Finding Zero 吴秋实 */ read (T); while (T--) { read (n); f = 1; mx = -1; out = tot = 0; for (int i = 3; i <= n; ++i) keko[i] = ask (1, 2, i); for (int i = 3; i <= n; ++i) { if (i > 3 && keko[i - 1] != keko[i]) f = 0; if (keko[i] > mx) mxfr = i, mx = keko[i]; } if (f) { for (int i = 1; i <= n - 2; ++i) keko[i] = ask (i, i + 1, i + 2); keko[n - 1] = ask (n - 1, n, 1); keko[n] = ask (n, 1, 2); mx = -1; for (int i = 1; i <= n; ++i) mx = max (mx, keko[i]); for (int i = 1; i <= n; ++i) if (keko[i] == mx && keko[proc (i + 1)] == mx) { outp (proc (i + 1), proc (i + 2)); out = 1; break; } if (!out) outp (1, 2); continue; } for (int i = 1; i <= n; ++i) { if (i == mxfr) continue; ++tot; if (i == proc (mxfr - 1)) pos[tot][0] = i, pos[tot][1] = proc (mxfr + 1); else pos[tot][0] = i, pos[tot][1] = proc (i + 1); keko[tot] = ask (mxfr, pos[tot][0], pos[tot][1]); } mx = -1; for (int i = 1; i <= tot; ++i) mx = max (mx, keko[i]); for (int i = 1; i <= tot; ++i) { int pre = i - 1 ? i - 1 : tot; if (keko[i] == mx && keko[pre] == mx) { outp (mxfr, publ (pos[i], pos[pre])); out = 1; break; } } if (!out) outp (1, 2); } return 0; }
  • 2022年2月7日