Solution -「CF 1370F2」The Hidden Pair (Hard Version)
这是一道交互题。
给定一棵 个结点的树,其中有两个是特殊结点。每次你可以提出形如 的询问,交互器会回答在点集 中,到两个特殊结点距离之和最小的结点 和这个最小距离和 (若有多个 ,回答任意一个)。你需要猜出两个特殊结点的编号。
, 组数据,询问次数上限为 次。
第一次询问,显然问所有的 个点,就能得到特殊点 间的距离 和 路径上的一个结点 。
以 为根, 显然在 的两棵子树内。接下来,以 为圆心“画圆”——询问所有到 的距离为某一定值 的点集,就能得到一个新的距离 。若 ,说明 离 较远的一个点到 的距离 ,否则,就 ,所以可以二分 ,花 次询问找到离 较远的那个特殊点。
最后,设较远点 到 的距离为 ,把到 距离为 的所有点拿出来再问一次就得到另一个特殊点 了(注意排除掉在 到 路径上的点)。
但是,最坏情况会有 次询问,刚好多一次 qwq。
不过二分找到是“较远点”,所以其到 的距离一定在 之间,取这个区间作为二分上下界。其大小显然不超过 ,所以刚好能节约一次二分的询问。
复杂度 。
/* Clearink */
#include <cstdio>
#include <vector>
#include <assert.h>
inline int rint () {
int x = 0, f = 1; char s = getchar ();
for ( ; s < '0' || '9' < s; s = getchar () ) f = s == '-' ? -f : f;
for ( ; '0' <= s && s <= '9'; s = getchar () ) x = x * 10 + ( s ^ '0' );
return x * f;
}
const int MAXN = 1000;
int n, ecnt, mxd, head[MAXN + 5], fa[MAXN + 5], dep[MAXN + 5];
std::vector<int> all, eqdis[MAXN + 5];
bool ban[MAXN + 5];
struct Edge { int to, nxt; } graph[MAXN * 2 + 5];
inline void link ( const int s, const int t ) {
graph[++ ecnt] = { t, head[s] };
head[s] = ecnt;
}
inline void collect ( const int u, const int d ) {
eqdis[dep[u] = d].push_back ( u ), mxd = d < mxd ? mxd : d;
for ( int i = head[u], v; i; i = graph[i].nxt ) {
if ( ( v = graph[i].to ) ^ fa[u] ) {
fa[v] = u, collect ( v, d + 1 );
}
}
}
inline void inter ( const std::vector<int>& pts, int& p, int& dis ) {
if ( pts.empty () ) return void ( p = dis = 0 );
printf ( "? %d", ( int ) pts.size () );
for ( int u: pts ) printf ( " %d", u );
putchar ( '\n' ), fflush ( stdout );
assert ( ~( p = rint (), dis = rint () ) );
}
inline void clear () {
ecnt = mxd = 0, all.clear (), eqdis[0].clear ();
for ( int i = 1; i <= n; ++ i ) {
head[i] = dep[i] = fa[i] = ban[i] = 0;
eqdis[i].clear ();
}
}
int main () {
char rep[20];
for ( int T = rint (); T --; ) {
clear ();
n = rint ();
for ( int i = 1, u, v; i < n; ++ i ) {
all.push_back ( i );
u = rint (), v = rint ();
link ( u, v ), link ( v, u );
}
int p, dis;
all.push_back ( n ), inter ( all, p, dis );
collect ( p, 0 );
int l = dis + 1 >> 1, r = mxd < dis ? mxd : dis, S = 0;
while ( l < r ) {
int mid = l + r + 1 >> 1, curp, curd;
inter ( eqdis[mid], curp, curd );
if ( curd > dis ) r = mid - 1;
else S = curp, l = mid;
}
if ( !S ) inter ( eqdis[l], S, r );
int oth = dis - dep[S], Q, tmp;
for ( int u = S; u ^ p; u = fa[u] ) ban[u] = true;
for ( auto it ( eqdis[oth].begin () ); it != eqdis[oth].end (); ++ it ) {
if ( ban[*it] ) {
eqdis[oth].erase ( it );
break;
}
}
inter ( eqdis[oth], Q, tmp );
printf ( "! %d %d\n", S, Q ), fflush ( stdout );
scanf ( "%s", rep ), assert ( rep[0] == 'C' );
}
return 0;
}
一眼出 次询问的方法然后卡了半天 qwq……养成卡二分上下界的习惯对常数有极大好处。(
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现