CF1624D – Finding Zero – 题解与思路重现
给大家表演一个一场下青
CodeForces Round 770 Div.2 Problem D – Finding Zero
一道非常有意思的交互和构造题。
考虑能否固定两个数,通过它们的相对大小关系,确定其他数中是否存在或最大值。
经过一番推导,我们发现,设的情况下,对于三元组,有。所以,设以上式结果为因变量,为自变量,应有以下图像:
容易发现,在给定的序列当中取,当取到最大值时,为或者。令。现在我们就已经知道了数列中其中一个最值的位置。那么,如何判别这是还是呢?
设该最值的位置为。容易发现,不论是还是,设为另一个最值的位置,则有在或时取到最大值,否则与题设和定义相矛盾。同时,由于题目要求输出至多两个可能存在的位置,我们需要更准确地判定另一个最值的位置。则我们枚举,令,计算,则由于和两个结果均会取到最大值,故我们可以由此确定的位置。这时答案就是。
还有一个比较棘手的问题:当怎么办?一开始(赛时)我采取分类讨论的想法,发现出现这种情况,只可能是因为
- 中既包含又包含(下图粉红色点集);
- 中包含,且(下图绿色点集);
- 中不包含,但(下图浅蓝色点集)。
为什么不包含“是,”的情况?因为这样一来,由题意可得,与题设相矛盾。
再来考虑有什么办法能够将这三种情况统一起来。最开始,我考虑选出,分别计算,然后取的最小值,认为其补集就是最值所在位置(例如,若取到最小,则是最值)。但这种方法在第三种情况会出问题。
但今天我发现,这三种情况有一个共性:最大值和最小值紧挨在一起,即或。第一种情况无需证明;对于情况二,无论还是,都有;第三种情况,容易证明。所以,我们直接计算所有三元组,找到的最大值,和上述一种情况一样选择产生的,计算和代表的三元组的交集即可。
总体来看,对于情况A,我们使用了次询问;情况B则采用了恰好次询问。
实现时应当注意各种细节。Submission #145491065
- 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
#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; }