Typesetting math: 100%

[题解]CF1990E2 Catch the Mole(Hard Version)

思路

我们先随便选择一个叶子结点,查询 BB 次。如果是返回的结果是 11,说明鼹鼠就在这个叶子结点;否则它将向上跳 BB 次。

此时,我们得到一个关键结论,如果一棵子树最大深度小于等于 BB,那么鼹鼠一定不在这棵子树中,因为鼹鼠无论如何都跳了 BB 次。

我们希望找到鼹鼠行动的链,找到了就可以直接二分找到其位置。

令当前到达的节点为 uu,查询所有 uu 的子节点 vv,并把最大深度小于等于 BB 的子树跳掉。当 vv 查询出来过后结果为 11,说明此时鼹鼠在 vv 子树中,向下递归即可。

但是,这样 naive 的想法显然是有问题的,可以被链给创飞。

观察到,当我们即将查询 uu 的最后一个满足最大深度大于 BB 的子树时,前面所有的子树都不行,那么鼹鼠一定在 vv 子树。

这样做的好处就是,每一次递归都会减少一个深度大于 BB 的子树,那么最多只会查询 nBnB 次。

接下来就只需要在我们找到的这条链上二分即可。

查询次数是 B+nB+lognB+nB+logn 的,当 B=nB=n 时取得最小值 2n+logn2n+logn

Code

#include <bits/stdc++.h>
#define re register

using namespace std;

const int N = 5010;
int n,B;
int d[N];
vector<int> v,g[N];

inline int read(){
    int r = 0,w = 1;
    char c = getchar();
    while (c < '0' || c > '9'){
        if (c == '-') w = -1;
        c = getchar();
    }
    while (c >= '0' && c <= '9'){
        r = (r << 3) + (r << 1) + (c ^ 48);
        c = getchar();
    }
    return r * w;
}

inline bool ask(int u){
    printf("? %d\n",u); fflush(stdout);
    return read();
}

inline void print(int u){
    printf("! %d\n",u); fflush(stdout);
}

inline void dfs1(int u,int fa){
    d[u] = 1;
    for (int v:g[u]){
        if (v == fa) continue;
        dfs1(v,u); d[u] = max(d[u],d[v] + 1);
    }
}

inline void dfs2(int u,int fa){
    v.push_back(u);
    vector<int> s;
    for (int v:g[u]){
        if (v == fa || d[v] <= B) continue;
        s.push_back(v);
    }
    for (re int i = 1;i < s.size();i++){
        int v = s[i];
        if (ask(v)) return dfs2(v,u);
    }
    if (!s.empty()) dfs2(s.front(),u);
}

inline void solve(){
    v.clear();
    n = read(); B = sqrt(n);
    fill(d + 1,d + n + 1,0);
    for (re int i = 1;i <= n;i++) g[i].clear();
    for (re int i = 1,a,b;i < n;i++){
        a = read(),b = read();
        g[a].push_back(b); g[b].push_back(a);
    }
    for (re int i = 2;i <= n;i++){
        if (g[i].size() == 1){
            for (re int j = 1;j <= B;j++){
                if (ask(i)) return print(i);
            }
            break;
        }
    }
    dfs1(1,0); dfs2(1,0);
    int l = 0,r = v.size() - 1;
    while (l < r){
        int mid = l + r + 1 >> 1;
        if (ask(v[mid])) l = mid;
        else{
            r = max(0,mid - 2);
            if (l) l--;
        }
    }
    print(v[l]);
}

int main(){
    int T; T = read();
    while (T--) solve();
    return 0;
}

作者:WaterSun

出处:https://www.cnblogs.com/WaterSun/p/18324262

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

posted @   WBIKPS  阅读(10)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
more_horiz
keyboard_arrow_up dark_mode palette
选择主题
点击右上角即可分享
微信分享提示