【luogu CF1370F】The Hidden Pair(构造)

The Hidden Pair

题目链接:luogu CF1370F1 / luogu CF1370F2

题目大意

给你一棵树,然后你要猜两个特殊点。
每次你可以询问一个点集,会告诉你这个点集中到两个特殊点距离之和最近的点以及这个距离和。
然后要你在至多 11 次操作猜出这两个点。
n1000

思路

首先看到这个操作次数跟 n 的关系不难猜到会用 log 次左右。
然后你不难想到一开始要选全图问一次,求出这两个特殊点之间的路径长度 d

那第一次给的点有什么用呢?你会发现它是两个特殊点之间的路径上的一个点。
然后我们进而能发现如果一个点在这个路径上,那它到这两个点的路径长度就是这两个点之间的长度,否则就会要大于它。

然后我们考虑能不能求出其中的一个点。(因为你找到之后把距离它为 d 的点都询问一次,得到的肯定是另一个点,毕竟别的点必定不在路径上)
考虑一个神奇的方法就是二分。

你考虑把你第一次给的点当做根,然后去找那个深度较大的点。
那你会发现你如果二分一个深度,每次询问把这个深度的所有点都拿去询问,那如果得到的点的距离和是 d,也就是说这个深度还有点在两个点的路径中,否则就没了。
那我们找到最大的深度的时候,这个点必定是路径其中的一个端点。

然后我们看看操作次数:
一开始一次,中间 log 次,最后一次:1+10+1=12 刚好超了一个。
(这个时候简单版已经能过了)

那怎么优化呢?其实有个小小的性质,就是在二分的时候,你下界 l 不一定要从 0 开始。
毕竟你路径长度为 d,你还要找深度大的点,那它至少的深度会在 d2,那把 l 一开始弄成这个,就刚好少了一次操作!

代码

#include<cstdio> #include<vector> #define clean() fflush(stdout) using namespace std; const int N = 1000 + 10; int TT, n, x, y, d, deg[N], S, T; vector <int> G[N], ds[N]; char s[11]; void dfs(int now, int father) { ds[deg[now]].push_back(now); for (int i = 0; i < G[now].size(); i++) { int x = G[now][i]; if (x == father) continue; deg[x] = deg[now] + 1; dfs(x, now); } } bool check(int x) { if (!ds[x].size()) return 0; printf("? %d", ds[x].size()); for (int i = 0; i < ds[x].size(); i++) printf(" %d", ds[x][i]); printf("\n"); clean(); int rx, ry; scanf("%d %d", &rx, &ry); if (ry == d) {S = rx; return 1;} return 0; } int main() { scanf("%d", &TT); while (TT--) { scanf("%d", &n); for (int i = 1; i < n; i++) { scanf("%d %d", &x, &y); G[x].push_back(y); G[y].push_back(x); } printf("? %d", n); for (int i = 1; i <= n; i++) printf(" %d", i); printf("\n"); clean(); scanf("%d %d", &x, &d); for (int i = 1; i <= n; i++) ds[i].clear(); deg[x] = 0; dfs(x, 0); int l = (d + 1) / 2, r = d; while (l <= r) { int mid = (l + r) >> 1; if (check(mid)) l = mid + 1; else r = mid - 1; } for (int i = 1; i <= n; i++) ds[i].clear(); deg[S] = 0; dfs(S, 0); printf("? %d", ds[d].size()); for (int i = 0; i < ds[d].size(); i++) printf(" %d", ds[d][i]); printf("\n"); clean(); int tmp; scanf("%d %d", &T, &tmp); printf("! %d %d\n", S, T); clean(); scanf("%s", s + 1); if (s[1] == 'I') return 0; for (int i = 1; i <= n; i++) G[i].clear(); } return 0; }

__EOF__

本文作者あおいSakura
本文链接https://www.cnblogs.com/Sakura-TJH/p/luogu_CF1370F.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   あおいSakura  阅读(19)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
历史上的今天:
2021-03-27 【ybt金牌导航8-7-2】周期字符串 / 关于莫比乌斯反演的少量知识
点击右上角即可分享
微信分享提示