视频加载失败

安吉D11-D

1218 字
6 分钟
安吉D11-D
原题呈现

Codeforces 2193G - Paths in a Tree#

题目描述#

本题为交互式题目.

给你一个无环连通的无向图,包含 nn 个顶点. 定义一条从顶点 vv 到顶点 uu 的路径为一个由互不相同的顶点组成的序列 p1,p2,,pkp_1, p_2, \dots, p_k,满足 p1=vp_1 = vpk=up_k = u,并且对于所有 ii1i<k1 \le i < k),顶点 pip_ipi+1p_{i+1} 之间都存在一条边.

图中有两个隐藏的顶点 xxyy(它们可能相同).你可以进行如下询问:

  • 选择两个顶点 a,ba, b1a,bn1 \le a, b \le n). 交互库会返回 11,如果从 xxyy 的路径与从 aabb 的路径至少有一个公共顶点;否则返回 00

你的任务是在不超过 n/2+1\lfloor n/2 \rfloor + 1 次询问内,找到至少一个位于 xxyy 路径上的顶点.

注意:交互库是自适应的,这意味着隐藏的顶点可能会根据你的询问而改变,但不会与先前的回答矛盾.

输入格式#

每个测试包含多个测试用例. 第一行包含一个整数 tt1t1041 \le t \le 10^4)—— 测试用例的数量.接下来的行描述每个测试用例.

每个测试用例的第一行包含一个整数 nn2n21052 \le n \le 2 \cdot 10^5)—— 图中顶点个数.

接下来 n1n-1 行,每行包含两个整数 v,uv, u1v,un1 \le v, u \le n),表示顶点 vvuu 之间有一条边.

保证所有测试用例的 nn 之和不超过 21052 \cdot 10^5

交互过程#

为了找到路径上的顶点,你最多可以使用 n/2+1\lfloor n/2 \rfloor + 1 次询问. 询问格式为 ? a b

每次询问后,读入一个整数,即返回的 0011

当你找到一个满足要求的顶点时,输出一行 ! v1vn1 \le v \le n),其中 vv 是你找到的顶点.

如果你的程序对某个测试用例发出了超过 n/2+1\lfloor n/2 \rfloor + 1 次询问,那么交互库会返回 1-1.在收到这样的回答后,你的程序应该立即终止,否则将得到“答案错误”的评判.否则,它可能会得到其他评判结果.

输出询问后,记得输出换行并刷新输出缓冲区,否则你会收到“超时”的评判. 在 C++ 中可使用 fflush(stdout)cout.flush();在 Java 中可使用 System.out.flush();在 Python 中可使用 stdout.flush();其他语言请参考相应文档.

样例#

输入#

3
2
1 2
1
3
1 2
1 3
0
0
4
1 2
2 3
2 4
0
1

输出#

? 1 1
! 1
? 1 1
? 2 2
! 3
? 1 3
? 4 4
! 4

首先,观察到询问次数的限制是 n/2+1\lfloor n/2 \rfloor + 1,这说明我们每次至少要排除两个节点.

有以下一个结论:我们可以按照 dfn 次序,每一次询问 dfn 最靠前的两个没有被询问过的节点.如果回答是 Yes,则再花一次询问分出是询问的两个点其中哪一个点.如果回答 No 则接着询问另外两个 dfn 最靠前且没有被询问过的节点

为什么是这样呢?我们观察 dfn 连续的两个节点,发现它们只会存在两种位置关系:

  • 一条边的两个顶点(如下图的 (1,2)(1,2)(2,3)(2,3)
  • 分居于一个节点的两颗子树(如下图的 (4,5)(4,5)(6,7)(6,7)

1

2

7

3

5

8

9

4

6

1

2

7

3

5

8

9

4

6

对于第一种情况,由于本身就是一条边,不会有其他点干扰.因此,可以稳定地排除两个点;对于第二种情况,我们可以发现,对于一对点,其路径上的其他点必然在前面的询问中已经排除了.如 (6,7)(6,7) 这一对点,其路径上的 1,2,51,2,5 都已经在前面的询问中被排除过了.因此也可以稳定地排除两个点.

标程

#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 100;
int n, dfn[N], cnt, id[N];
vector<int> e[N];
void dfs(int u, int fa) {
dfn[u] = ++cnt;
id[dfn[u]] = u;
for (auto v : e[u]) {
if (v != fa)
dfs(v, u);
}
}
void solve() {
for (int i = 1; i <= n; ++i)
e[i].clear();
cin >> n;
cnt = 0;
int root = -1;
for (int i = 1; i <= n - 1; i++) {
int u, v;
cin >> u >> v;
if (root == -1) {
root = u;
}
e[u].push_back(v);
e[v].push_back(u);
}
dfs(root, 0);
for (int i = 1; i + 1 <= n; i += 2) {
cout << "? " << id[i] << " " << id[i + 1] << endl;
cout.flush();
int res;
cin >> res;
if (res == 1) {
cout << "? " << id[i] << " " << id[i] << endl;
cout.flush();
cin >> res;
if (res) {
cout << "! " << id[i] << endl;
cout.flush();
return;
} else {
cout << "! " << id[i + 1] << endl;
cout.flush();
return;
}
}
}
cout << "! " << id[n] << endl;
cout.flush();
}
signed main() {
int t;
cin >> t;
while (t--) {
solve();
}
return 0;
}

文章分享

如果这篇文章对你有帮助,欢迎分享给更多人!

安吉D11-D
https://blog.jerrylab.top/posts/problem/anji2026/D11/D/
作者
Jerry
发布于
2026-08-12
许可协议
CC BY-NC-SA 4.0
Profile Image of the Author
Jerry
Hello, I'm Jerry.
公告
欢迎来到我的博客!这是一则示例公告。
分类
标签
最新动态
站点统计
文章
80
分类
3
标签
35
总字数
89,198
运行时长
0
最后活动
0 天前
站点信息
构建平台
Cloudflare Pages
博客版本
Firefly v6.16.5
文章许可
CC BY-NC-SA 4.0