Codeforces Round #651 (Div. 2) 全文见:https://blog.csdn.net/qq_43461168/article/details/112577099
F. The Hidden Pair
题意:给定一个数。给定一个s和t,保证他们不一样。然后每次可以询问一个点集合。 会返回这个点集合中。dis(s,x)+dis(x,t)最小的点。也就是到他们的距离和最小。如果有多个,随机返回一个。并且告知dis(s,x)+dis(x,t) 这个值。问能否在 14(easy)/11(hard)次询问之内找出 s,t是哪两个点。
思路参考:https://blog.csdn.net/qq_45458915/article/details/106909780
思路:第一次询问。因为啥信息也没有。只能询问所有的点。这样就可以得到一个在 s-t 的最短路径上的某一个点,并且获得了 len = dis(s,t)。然后以这个点rt为根,dfs把到根距离相等的点放到同一堆。然后需要找出。距离根节点深度最大的并且到 dis(s,x)+dis(x,t) 距离等于 len 的点。就是这个过程。当然不可能遍历深度。 答案复杂度显然和log有关。 所以可以二分深度。 直到找到一个深度最大的,距离为len 的点。 那么这个点,必然是s或者t中的某一个。 然后以这个点为根。建树。然后再询问一次,找到深度为 len 并且距离为len 的点。就是另一个点了。easy版的 可以询问14次。 二分的起点设为l=1,r=n,就能过。hard版少几次。因为第一次和最后一次查询省不掉。只能优化二分的过程。对于左边界。设为 (len+1)/ 2。为什么呢,因为 len是s 到 t 的距离。rt又是 s,t之间的点。那么再去找距离rt最远 而到s和t距离和等于len 的点时,这个最大深度,必然不可能小于(len+1)/ 2啊。 也就是整条路径被rt切成两段了。而找的是更长的那条。所以左端点就优化了。 对于右端点,r不可能大于 min(len,max_dep)。dfs 的时候记录 max_dep,就行了。 因为不可能找到大于len 的点去。 那不显然不成立。 同时距离根节点最远的也就是max_dep,这样右端点就优化了。可以过hard。
AC代码:
#include <iostream>
#include <bits/stdc++.h>
#define int long long
#define mk make_pair
#define gcd __gcd
#define pb push_back
using namespace std;
const double eps = 1e-10;
//const int mod = 1e9+7;
const int mod = 998244353 ;
const int N = 3e5+7;
int n,m,k,t = 1,cas = 1;
int a[N],b[N];
int maxx = 0 ;
vector<int> edge[N];
vector<int> depp[N];
void dfs(int pos,int dep,int fa){
maxx = max(maxx,dep);
depp[dep].pb(pos);
for(int i = 0 ; i < edge[pos].size() ; i ++){
int to = edge[pos][i];
if( to != fa){
dfs(to,dep+1,pos);
}
}
}
pair<int,int> ask(vector<int> vt){
cout<<"? "<<vt.size()<<" ";
for(int i = 0 ; i < vt.size() ; i ++){
cout<<vt[i]<<" ";
}
cout<<endl;
cout.flush();
int rt,len; cin>>rt>>len;
return {rt,len};
}
signed main(){
cin>>t;
while(t--){
maxx = 0;
cin>>n;
for(int i = 1 ; i <= n ; i ++) depp[i].clear(),edge[i].clear();
for(int i = 0 ; i < n-1 ; i ++){
int x,y;
cin>>x>>y;
edge[x].pb(y);
edge[y].pb(x);
}
cout<<"? "<<n<<" ";
for(int i = 1 ; i <= n ; i ++) cout<<i<<" ";
cout<<endl;
cout.flush();
int rt,len;cin>>rt>>len;
dfs(rt,0,-1);
int l = (len+1)/2, r = min(maxx,len);
int new_rt;
while(l <= r){
int mid = (l+r)>>1;
pair<int,int> tmp = ask(depp[mid]);
if(tmp.second == len){
new_rt = tmp.first;
l = mid+1;
}else{
r = mid-1;
}
}
for(int i = 1 ; i <= n ; i ++) depp[i].clear();
dfs(new_rt,0,-1);
pair<int,int> res = ask(depp[len]);
cout<<"! "<<res.first<<" "<<new_rt<<endl;
cout.flush();
string s;
cin>>s;
}
return 0;
}