【Codeforces Round #668 (Div. 2) D】Tree Tag

题目链接

点我呀

翻译

给你一棵树,一个人(\(Alice\))在 \(a\) 处,一个人(\(Bob\))在 \(b\) 处,其中 \(a\) 每次可以移动到距离(经过的边的个数)为 \(da\) 以内的任意一个点上,\(b\) 每次可以移动到距离为 \(db\) 以内的任意一个点上。

\(a\) 上的人 \(Alice\) 先移动,问 \(A\) 能否在有限次的移动过后和 \(Bob\) 在同一个节点上,如果能够做到, 那么 \(A\) 赢,否则 \(B\) 赢。\(A\)\(B\) 都会采取有利于自己的最佳策略。

问你最后谁会赢。

题解

我们分成 \(4\) 类情况讨论:

  • \(dis(a,b)\le da\), 那么显然 \(Alice\) 只需走一次,就能直接抓到 \(Bob\), 因此 \(Alice\) 赢。
  • \(2\times da\ge diameter(T)\), 其中 \(diameter(T)\) 表示树的直径。这种情况下, 只要 \(Alice\) 走到树的直径的中点上,那么下一次轮到 \(Alice\) 走的时候,她就可以走到树上的任意一个位置了,\(Bob\)也就无处藏身了,因此 \(Alice\) 赢。
  • \(db > 2\times da\), 因为已经排除了第一种情况,所以我们可以保证 \(Bob\) 能至少移动那么一次。我们可以采取这样的策略: 在 \(Alice\) 没有到达 \(Bob\)\(da\) 范围以内,都按兵不动。一旦到了这个范围内,那么
    \(Bob\)\(Alice\) 的距离 \(d(a,b)\le da\),那么我们马上让 \(Bob\) 走到一个和 \(Alice\) 距离为 \(da+1\) 的点 \(v\) 处, 因为 \(d(b,a)\le da\), 那么 \(d(b,a)+d(a,v)\le da + da +1\), 即 \(d(b,v)\le 2\times da+1\), 而 \(db> 2\times da\),
    所以 \(Bob\) 是肯定能一次移动到这么一个点 \(v\) 的。那么 \(Bob\) 也就每次都能在被抓的边缘疯狂试探了:), 因此 \(Bob\) 赢。这里保证存在这么一个点 \(v\) 的前提条件就是我们已经排除了第二种情况,不会出现所有的点 \(Alice\) 都能在一步之内走到。
  • \(db \le 2\times da\), 这种情况,我们可以把 \(Alice\) 所在的点 \(a\) 看做是树的根节点,\(b\) 是在 \(a\) 的某个子树中的 (距离根节点 \(v\) 的距离大于 \(da\) ,而且会发现,因为 \(db \le 2\times da\), 所以 \(a\) 永远无法跳出它所在的子树到达另外一棵子树中去 (跳过去了也只会被抓到)。
    所以,我们只要每次让 \(Alice\)\(b\) 所在的子树下走一个节点 (这个时候 \(d(a,b)\) 最坏也只是变成 \(2\times da\), 那么 \(b\) 依靠最大为 \(2\times da\)\(db\) 还是跳不出对应的子树),然后一步一步逼近 \(b\) 就可以了,所以这种情况仍然是 \(Alice\) 赢。

求出 \(a\)\(b\) 的距离和 \(da\) 判断一下,然后求出树的直径, 和 \(2\times da\) 比较一下, 最后用 \(2 \times da\) 再和 \(db\) 比较一下就可以了。

代码

#include <bits/stdc++.h>
using namespace std;

const int N = 1e5;

int n,a,b,da,db,d1,d2,nextX;
vector<int> g[N + 10];

void dfs1(int x,int fa,int mydis){
    if (x == b){
        d1 = mydis;
        return;
    }
    int len = g[x].size();
    for (int i = 0;i < len; i++){
        int y = g[x][i];
        if (y == fa){
            continue;
        }
        dfs1(y,x,mydis + 1);
    }
}

void dfs2(int x,int fa,int mydis){
    if (mydis > d2){
        nextX = x;
        d2 = mydis;
    }
    int len = g[x].size();
    for (int i = 0;i < len; i++){
        int y = g[x][i];
        if (y == fa){
            continue;
        }
        dfs2(y,x,mydis+1);
    }
}

int main(){
    #ifdef LOCAL_DEFINE
        freopen("E://9.AlgorithmCompetition//Visitor.txt","r",stdin);
    #endif
    ios::sync_with_stdio(0),cin.tie(0);
    int T;
    cin >> T;
    while (T--){
        for (int i = 1;i <= n; i++){
            g[i].clear();
        }
        cin >> n >> a >> b >> da >> db;
        for (int i = 1;i < n; i++){
            int x, y;
            cin >> x >> y;
            g[x].push_back(y);
            g[y].push_back(x);
        }
        dfs1(a, -1, 0);
        if (da >= d1){
            cout << "Alice" << endl;
            continue;
        }
        d2 = 0;nextX = 1;
        dfs2(a,-1,0);
        d2 = 0;
        dfs2(nextX,-1,0);
        if (2*da >= d2){
            cout << "Alice" << endl;
            continue;
        }
        if (db > da*2){
            cout << "Bob" << endl;
        }else{
            cout << "Alice" << endl;
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值