P8347 题解
liangbowen · · 题解
前言
题目传送门!
更好的阅读体验?
困难的博弈论题目。参考了巨佬 @Kaenbyou_Rin 的题解,并对他题解中错误的地方进行了更改。
警告:以下内容稍长,请认真阅读,有一定数学基础就很容易理解,因为实际证明难度不高。这个是感性证明。
思路
首先给出结论:只要有一个节点的度是偶数,先手必胜;否则后手必胜(也就是:后手必胜当且仅当全部节点的度都是奇数)。
证明如下。为了方便叙述,设状态
首先很显然,状态
因为
这样不管选哪个连通块,必定有一个点度为偶数(也就是原本与
接着证明:不是
这个其实也不难,我们容易想到,必定有一个度为偶数的点,它有一棵子树,里面的所有点,度数都是奇数。
事实上,如果一个点不行,就换另一个点,依次枚举即可找到。
这一部分的具体证明:其实特别简单。
第一次指定根时我们有一棵树,然后如果没有满足要求的子树的话,我们直接从下往上找到深度最大的度为偶数的点。
这个点既然深度最深,那它下面必定都是度为奇数的点。这一部分就证掉了。
那我们知道这个有什么用呢?更简单了,直接通过删除其他点,保留下这个根以及全是奇点的子树。那么根的度就会变成
其实认真阅读上述内容,并不是很困难。如果不仔细证明的话,实际上几分钟就能想出以上两个结论。
有了这两个结论,接下来就很容易了吧。说白了就是:
阅读题目,当对手将状态变为了
也就是说,如果初始状态为非
由于每次
反过来,先手获得了
其实很简单,对吧?
完整代码
十分简单精简。
#include <iostream>
#include <cstdio>
using namespace std;
int in[100005];
void solve()
{
int n;
scanf("%d", &n);
for (int i = 1; i <= n; i++) in[i] = 0;
for (int i = 1; i < n; i++)
{
int u, v;
scanf("%d%d", &u, &v);
in[u]++, in[v]++;
}
for (int i = 1; i <= n; i++)
if (in[i] % 2 == 0) //度为偶数,先手必胜
{
puts("Hifuu");
return;
}
puts("Luna");
}
int main()
{
int T;
scanf("%d", &T);
while (T--) solve();
return 0;
}
希望能帮助到大家!