题解 CF1167B 【Lost Numbers】

rui_er

2020-08-10 23:53:46

Solution

我们有一个由 $4,8,15,16,23,42$ 组成的排列,只能询问四次两个位置上的数的乘积。 下面是一些可能会考虑到的**错误的**策略(也是我做题时的思路),在这里放出体现我们的思考过程: **策略 1**:每次询问两个相同的下标 $i$ 和它本身。 这种策略是最容易想到的,但是这样的话只能询问出 $4$ 个位置的值,剩下两个位置就没办法了。考虑其它策略。 **策略 2**:询问 $1$ 与 $1$ 的乘积,得到第一个数,此后三次询问 $(1,2)(2,3)(3,4)$。 和策略 1 有着同样的问题。 --- **正确策略** 通过上面的思考,我们得到了一个结论:**只有 $4$ 个位置是显然不行的**,考虑添加第五个位置。 我们可以询问 $(1,2)(2,3)(3,4)(4,5)$,这样就能方便的获得和五个位置相关的信息。 **具体解法** 在按照上面的策略询问完前五个数后得到了四个答案(代码中记做 $mutiple$),我们枚举所有 $6!=720$ 种可能的排列情况,对于每个排列,循环一遍判断是否符合询问得到的答案即可。 但是这样还不够,我们还要知道**满足四个答案的排列是唯一的**。 **唯一性证明** 自己口胡的,可能不太严谨,但是自认为比较好理解,可以手玩几个理解一下。 因为上面数列任意两个数的乘积都不同,我们想到,满足 $a_i\times a_j=k$ 的只有两种可能,即 $i$ 与 $j$ 交换。而上面就相当于固定了前五个数,因为数列中的六个数给定,可以通过排除来获得第六个数的信息。 唯一性得证,策略正确。 --- 代码: ```cpp //By: Luogu@rui_er(122461) #include <bits/stdc++.h> using namespace std; int a[7] = {-1, 4, 8, 15, 16, 23, 42}, multiple[5]; int main() { for(int i=1;i<=4;i++) { printf("? %d %d\n", i, i+1); fflush(stdout); scanf("%d", &multiple[i]); } do { bool _ = true; for(int i=1;i<=4;i++) { if(a[i] * a[i+1] != multiple[i]) { _ = false; break; } } if(_) { printf("! "); for(int i=1;i<=6;i++) printf("%d ", a[i]); puts(""); break; } }while(next_permutation(a+1, a+7)); return 0; } ```