正题
题目链接:https://www.luogu.com.cn/problem/CF1370F2
题目大意
TTT组数据,给出nnn个点的一棵树,有两个隐藏的关键点。你每次可以询问一个点集,交互库会回答这个点集中的一个点满足它到两个关键点的距离和最小,和这个距离。
要求在111111次询问内求出这两个关键点。
1≤T≤10,1≤n≤10001\leq T\leq 10,1\leq n\leq 10001≤T≤10,1≤n≤1000
解题思路
首先第一下不知道干啥就问整张图吧。
这样我们就得到了一个点rtrtrt和距离LLL。这个点rtrtrt一定在关键点u,vu,vu,v的路径上,且LLL表示u,vu,vu,v之间的距离。
然后就好搞了,我们以rtrtrt为根,考虑利用这个LLL来搞点操作,我们每次选择一个深度depdepdep然后询问所有这个深度的点的话,如果得到的距离等于LLL就表示这个深度有u∼vu\sim vu∼v路径上的点。
也就是我们可以通过二分得到最深的位置,而最深的位置一定是离rtrtrt较远的一个关键点uuu。
而我们又知道两个关键点的距离,以uuu为根询问一遍深度LLL的节点就可以得到vvv了。
二分上界是min{L,depmax}min\{L,dep_{max}\}min{L,depmax},所以次数是log(n)+2≤12log(n)+2\leq 12log(n)+2≤12。好像多了一次?
再挖掘一下性质,发现我们找的是离rtrtrt较远的一个关键点,所以这段距离一定是不小于⌊L−12⌋\lfloor\frac{L-1}{2}\rfloor⌊2L−1⌋的,这样就可以少去一次了
code
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
const int N=1100;
struct node{int to,next;
}a[N<<1];
int T,n,tot,ls[N],mx;
vector<int> v[N];char s[10];
void print(int x)
{if(x>9)print(x/10);putchar(48+x%10);return;}
void addl(int x,int y){a[++tot].to=y;a[tot].next=ls[x];ls[x]=tot;return;
}
void dfs(int x,int fa,int dep){v[dep].push_back(x);mx=max(mx,dep);for(int i=ls[x];i;i=a[i].next){int y=a[i].to;if(y==fa)continue;dfs(y,x,dep+1);}return;
}
int main()
{scanf("%d",&T);while(T--){memset(ls,0,sizeof(ls));tot=mx=0;for(int i=0;i<=n;i++)v[i].clear();scanf("%d",&n);for(int i=1;i<n;i++){int x,y;scanf("%d%d",&x,&y);addl(x,y);addl(y,x);}printf("? %d ",n);for(int i=1;i<=n;i++)print(i),putchar(' ');putchar('\n');fflush(stdout);int rt,L,u,uu;scanf("%d%d",&rt,&L);dfs(rt,0,0);u=uu=rt;int l=(L-1)/2+1,r=min(L,mx);while(l<=r){int mid=(l+r)>>1;printf("? %d ",v[mid].size());for(int i=0;i<v[mid].size();i++)print(v[mid][i]),putchar(' ');putchar('\n');fflush(stdout);int x,d;scanf("%d%d",&x,&d);if(d==L)l=mid+1,u=x;else r=mid-1;}v[L].clear();dfs(u,0,0);printf("? %d ",v[L].size());for(int i=0;i<v[L].size();i++)print(v[L][i]),putchar(' ');putchar('\n');fflush(stdout);scanf("%d%d",&uu,&L);printf("! %d %d\n",u,uu);fflush(stdout);scanf("%s",s+1);}
}