jia1saurus’s diary

TooDifficult

AGC014 D Black and White Tree(博弈论)

题目大意:

给出一个n个结点的树,A可以把树上的结点涂成白色,B可以把树上的结点涂成黑色,A和B交题涂色,全部涂完以后,B进行这样的操作

把树上所有与黑色结点相邻的点全部涂成白色

如果这样树上还有白色,那么A赢,否则B赢

(n <= 10^5)

 

题解:

我是这么考虑的,A需要一直保持主动才有机会赢

保持主动的意思就是说,A如果把一个点涂成白色,那么B必须把其附近的一个点涂成黑色,否则B就会输

如何保持主动呢?其实很简单,首先把树变成以1为根节点的树

然后如果A把叶子结点的父结点涂成白色,那么B一定要把叶节点涂成黑色。

同时可以证明这样做一定会更优,因为白色结点在黑色结点的上方。

所以A保持主动总是更优的。

如何判断输赢呢?

一个简单的条件是如果一个结点涂成白色以后,它有2个叶子结点,那么就是A赢

因为B无法同时涂2个叶子结点

根据这个,就有这样的做法

就是先把所有叶子结点的父结点都涂成白色,然后这个过程中如果有一个结点被涂了两遍,那么就是A赢

如果没有,就把这些染色的结点都删去,这样又会多很多叶子结点,继续向上这么做即可

注意对根节点的处理(或许不处理也可以通过)

#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <queue>
#define pb push_back
using namespace std;
const int maxn = 1e5 + 100;
queue<int> Q;
vector<int> G[maxn];
int f[maxn], col[maxn], in[maxn];
void dfs(int x, int fa){
    f[x] = fa;
    for(auto to : G[x]){
        if(to == fa) continue;
        dfs(to, x);
    }
}
int n, x, y;
int main()
{
    cin>>n;
    for(int i = 0; i < n-1; i++){
        cin>>x>>y;
        in[x]++; in[y]++;
        G[x].pb(y); G[y].pb(x);
    }
    dfs(1, 1);
    for(int i = 1; i <= n; i++){
        if(in[i] == 1) Q.push(i);
    }
    int label = 0;
    f[1] = 0;
    while(!Q.empty()){
        int x = Q.front(); Q.pop();
        if(col[x]) continue;
        if(col[f[x]]) label = 1;
        else{
            col[f[x]] = 1;
            in[f[f[x]]]--;
            if(in[f[f[x]]] == 0) label = 1;
            if(in[f[f[x]]] == 1 && f[f[x]] != 1) Q.push(f[f[x]]);
        }
    }
    cout<<(label ? "First" : "Second")<<endl;
    return 0;
}