题目链接: Hemose in ICPC ?
大致题意
给定一棵有 n n n个顶点, n − 1 n - 1 n−1条边的树.
定义: D i s t ( a , b ) = a , b 两 点 间 路 径 的 g c d 的 最 大 值 . Dist(a, b) = a, b两点间路径的gcd的最大值. Dist(a,b)=a,b两点间路径的gcd的最大值.
我们可以进行最多 12 12 12次询问, 每次询问给出一个点集, 系统会返回当前点集的所有点对中的最大 D i s t Dist Dist.
最终需要输出两个点 a , b a, b a,b, 要求最大化 D i s t ( a , b ) Dist(a, b) Dist(a,b).
解题思路
思维
我们首先对题目进行转化, 我们要求最大的 D i s t ( a , b ) Dist(a, b) Dist(a,b), 而 D i s t Dist Dist的求法是不断进行 g c d gcd gcd运算, 由于 g c d gcd gcd运算是只减不增的, 因此题目等价于求出两个点 a , b a, b a,b, 满足连接这两个点的边权是最大的.
同理, 每次询问时, 系统返回的答案我们可以看作是当前点集中的最大边权.
欧拉序
很显然, 我们首先对于所有点进行一次询问, 这样我们得到的 D m a x Dmax Dmax就是所求. 此时我们剩余 11 11 11次询问.
我们考虑到最大边权一定出现在两点之间, 因此不妨求出整棵树的欧拉序, 考虑在欧拉序上二分即可.
由于点最多有 1000 1000 1000个, 因此欧拉序最长为 1999 1999 1999, 我们可以在 11 11 11次询问后求得.
AC代码
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 1; i <= (n); ++i)
using namespace std;
typedef long long ll;
const int N = 1E3 + 10;
vector<int> edge[N];
int oula[N * 2], ind;
void dfs(int x, int fa) {
for (auto& to : edge[x]) {
if (to == fa) continue;
oula[++ind] = to;
dfs(to, x);
oula[++ind] = x;
}
}
int main()
{
ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
int n; cin >> n;
rep(i, n - 1) {
int a, b; cin >> a >> b;
edge[a].push_back(b);
edge[b].push_back(a);
}
oula[++ind] = 1;
dfs(1, 0);
auto ask = [](int l, int r) {
set<int> st; //去重这部分区间的欧拉序
for (int i = l; i <= r; ++i) st.insert(oula[i]);
cout << "? " << st.size() << ' ';
for (auto& op : st) cout << op << ' ';
cout << endl;
int res; cin >> res;
return res;
};
int target = ask(1, ind);
int l = 1, r = ind;
while (l + 1 < r) {
int mid = l + r >> 1;
if (ask(l, mid) == target) r = mid;
else l = mid;
}
cout << "! " << oula[l] << ' ' << oula[r] << endl;
return 0;
}