agc010 F - Tree Game

题意:

给定一棵树,节点有权 \(a_i\)。初始甲选一个节点放上一个棋子,然后甲乙轮流操作(甲先),一次操作定义为:令当前棋子所在节点的权减一,然后把棋子移动到某邻点。不能操作(即轮到他时当前棋子所在节点权为0)者输。问甲一开始选哪些节点能赢

\(2\le n\le 3000\)

思路:

枚举甲一开始放棋子的点作为根节点。\(st_u=W/L\) 表示从 \(u\) 开始,只能在 \(u-\)子树中走,先手是必胜还是必败。那么

  1. 如果 \(u\) 存在一个儿子 \(v\),满足 \(a_v<a_u\)\(st_v=L\),那么 \(st_u=W\)
  2. 否则,\(st_u=L\)

证明:

  1. 在该条件下,先手从 \(u\) 走到 \(v\),后手为了避免失败又走回 \(u\),这样重复下去由于 \(a_v<a_u\) 故先手必胜。注意 \(st_v=L\) 时减小 \(a_v\) 不会改变 \(st_v\)
  2. \(u\) 的儿子要么权值 \(\ge a_u\),那么后手每次走回 \(u\) 就能赢;要么是必胜点,那么后手往 \(v-\)子树中走就能赢
//代码就很简单了
bool dfs(int u, int fa) {
    bool st = 0;
    for(int v : G[u]) if(v != fa)
        if(a[v] < a[u] && !dfs(v, u)) st = 1;
    return st;
}

void sol() {
    cin >> n;
    for(int i = 1; i <= n; i++) cin >> a[i];
    for(int i = 1; i < n; i++) {
        int x, y; cin >> x >> y;
        G[x].pb(y), G[y].pb(x);
    }

    for(int i = 1; i <= n; i++)
        if(dfs(i, 0)) cout << i << ' ';
}
posted @ 2022-07-13 17:28  Bellala  阅读(20)  评论(0)    收藏  举报