#L3884. 「eJOI2022」寻找树根 / Where Is the Root?

    ID: 3737 交互题 1000ms 256MiB 尝试: 4 已通过: 2 难度: 10 上传者: 标签>树结构树的分治其他构造二分查找排序树上最近公共祖先

「eJOI2022」寻找树根 / Where Is the Root?

「eJOI2022」寻找树根 / Where Is the Root?

题目描述

这是一道交互题。

给定一个有 nn 个节点的树。树是满足任意两不同点之间恰好有一条简单路径的图。同时保证至少有一个节点,这个节点与至少三个节点直接有边相连。其中一个节点是根,你的任务是找到它。为了完成这个任务,你允许问如下格式的问题:

对于给定的点集 a1,a2,,ama_1, a_2, \ldots, a_m,检查它们的最近公共祖先是否在集合中。

如果所有 SS 中的点到根的路径都经过 vv,那么就称节点 vv 是点集 SS 的公共祖先。点集 SS 的最近公共祖先(LCA)就是点集 SS 的公共祖先中距离根最远的那个。

交互方式

通过读入一个整数 nn (4n5004 \leq n \leq 500) 开始整个交互过程,nn 表示节点个数。

接着读入 n1n-1 行。第 ii 行包含两个整数 ai,bia_i, b_i (1ai,bin1 \leq a_i, b_i \leq n),表示树上 aia_ibib_i 两点之间有一条边。

保证这 n1n-1 条边可以形成一棵树,并且至少有一个节点,这个节点与至少三个节点直接有边相连。

如果要询问的话,首先输出 ?,然后输出一个整数 mm,然后输出 mm 个互不相同的整数 a1,a2,,ama_1, a_2, \ldots, a_m (1mn,1ain1 \leq m \leq n, 1 \leq a_i \leq n),表示你想要检查这些节点的 LCA 是否在其中。

作为回答,如果它们的 LCA 是 a1,a2,,ama_1, a_2, \ldots, a_m 中的一个的话,交互器将会输出 YES,否则输出 NO

你可以最多问 10001000 个问题,但是根据你问的问题个数会得到不同的得分。输出答案不算一次询问。请查看「评分」一节了解细节。

当你确定了根的时候,先输出 !,然后输出一个整数 vv (1vn1 \leq v \leq n),表示根。然后终止你的程序。

在输出一次查询后,不要忘记输出换行并刷新输出缓冲区。为了实现这点,使用:

  • fflush(stdout) 或者 cout.flush(),对于 C++ 语言;
  • stdout.flush(),对于 Python 语言。

保证对于每个测试点,树的根是在交互过程之前确定的。换句话说,交互器是非自适应性的。

样例交互

Input:

7
4 1
1 2
4 3
3 5
3 6
4 7

Output:

? 2 5 6

Input:

NO

Output:

? 3 6 3 5

Input:

YES

Output:

? 2 1 7

Input:

NO

Output:

? 2 4 6

Input:

YES

Output:

! 4

隐藏的根是节点 44

在第一个询问中,5566 的 LCA 是 33,不在 5566 中,因此输出 NO

第二个询问中,3,5,63,5,6 的 LCA 是 33,所以输出 YES

第三个询问中,1177 的 LCA 是 44,所以输出 NO

第四个询问中,4466 的 LCA 是 44,所以输出 YES

在此之后,我们可以猜到根是节点 44,并且这是正确答案。

评分

详细子任务附加限制及分值如下表所示。

子任务编号 附加限制 分值
1 n9n \leq 9 7
2 n30n \leq 30 10
3 n500n \leq 500 83

在第一和第二个子任务中,你可以最多询问 10001000 次。

在第三个子任务中,令 kk 是你在任何测试数据上的最大询问次数。如果 k9k \leq 9,你会获得 8383 分。否则,你会获得 $\lfloor\max(10,83\cdot(1-\frac{\ln(k-6)}{7}))\rfloor$ 分。

计算第三个子任务得分的 C++ 代码如下:

((k <= 9) ? 83 : max(10, int(83 * (1 - log(k - 6.0) / 7))))