abc067d <博弈 + dfs>

发布时间 2023-07-10 11:01:00作者: O2iginal

D - Fennec VS. Snuke

// https://atcoder.jp/contests/abc067/tasks/arc078_b
// <博弈 + dfs>
// 关键点: 双方的最优策略一定是首先沿着两人间的最短路径进行染色, 
//      当最短路径上的节点都被染色后, 两人就不存在冲突(争夺节点染色)了,
//      此后仅能在各自的区域内将未染色的分支节点染色,
// 按照上述策略找到最短路径上的分界点(染色情况), 统计双方总染色数进行比较即可
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
using LL = long long;
const int N = 1e5 + 10, INF = 0x3f3f3f3f;
vector<int> g[N];
int n;
int c[N];  // 染色: 1 black, 2 white

// 第一次遍历, 得到1~n的最短路径长度len, 计算分界点位置(len-1)/2
// 递归返回时, 对最短路径上的节点按策略进行染色
int dfs1(int u, int pa, int d)
{
    if (u == n)  // 找到节点n
    {
        c[u] = 2;
        return (d-1) / 2;  // 除n外, 最短路径上染成白色的节点数
    }

    for (auto &v: g[u])
    {
        if (v == pa) continue;
        int t = dfs1(v, u, d+1);

        if (t != INF)
        {
            // 当子结点返回值为正数(非INF), 则染成白色
            // 若<=0, 则染成黑色
            if (t > 0) c[u] = 2;
            else c[u] = 1;
            return t - 1;  // 序号-1, 使得从白色过渡为黑色
        }
    }
    return INF;  // 不在最短路径上的节点返回INF
}

// 第二次遍历, 对余下的节点进行染色, 统计白色个数
void dfs2(int u, int pa, int &cnt)
{
    cnt ++;
    for (auto &v: g[u])
    {
        if (v == pa || c[v] == 2) continue;
        dfs2(v, u, cnt);
    }
}

void solv()
{
    cin >> n;
    int a, b;
    for (int i = 1; i < n; i ++)
    {
        cin >> a >> b;
        g[a].push_back(b);
        g[b].push_back(a);
    }
    dfs1(1, 0, 0);

    int cnt = 0;
    dfs2(1, 0, cnt);
    
    if (cnt > n/2) cout << "Fennec" << endl;
    else cout << "Snuke" << endl;
}

int main()
{
    ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    int T = 1;
	// cin >> T;
    while (T --)
    {
        solv();
    }
    return 0;
}