Codeforces 1370F. The Hidden Pair(二分/思维)

58 篇文章 0 订阅
35 篇文章 1 订阅
"这篇博客详细介绍了如何解决Codeforces Round #651 (Div. 2) 中的F题"The Hidden Pair"。通过首次询问获取最短路径信息,然后利用DFS构建树形结构,并使用二分查找优化查询次数,最终在限制次数内确定问题的答案。文章提供了完整的AC代码作为参考。"
摘要由CSDN通过智能技术生成

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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值