P8347 「Wdoi-6」另一侧的月 题解

P8347 「Wdoi-6」另一侧的月 题解

第一次自己思考出来紫题,题解纪念一下。


下面为大家讲解如何一步步推到最终结论:


首先,原树没有根,不妨设它的根为 \(1\),将它转化成有根的,便于操作。

为了方便描述,我们称将一个非根节点的点的父亲删去,保留含这个点的连通块这个操作为截取操作(就是保留一棵子树);相反,若是将一个非根节点的点删去,保留含这个点父亲的连通块,我们就称之为删除操作(就是把一棵子树丢掉)。

另外,我们称一个有偶数个儿子的节点为偶点,奇数个儿子的点为奇点。注意:所有叶子结点都算偶点。


我们可以先看深度为 \(2\)(根节点深度为 \(1\),下同)的树(菊花图,也就是特殊性质 \(A\))。容易发现,你肯定不能删根节点,那么就只能一个个删叶子结点。然后就能得出:如果有奇数个叶子结点,那么先手必败,偶数个叶子结点相反。

显然,如果原树有一个点所有儿子都是叶子结点,且这个点是偶点,那我们肯定不能将这个点的子树截取下来(不然就成了偶数个叶子结点的菊花图了,后手必败)。

而且我们还可以得出一个重要结论:如果原树有一个点所有儿子都是叶子结点,且这个点是奇点,那么先手一定可以直接把这个点的子树截取下来,那么后手就成为这个菊花图的先手,必败。所以如果有这么样的一个点,先手就必胜。所以,我们中途也一定要保证不能出现这种点,即如果原图没有这种点,中途就不能删任何叶子结点


接着,我们将深度推广为 \(3\)(这里假设只有深度为 \(3\) 的点才叫叶子结点,或者假设每个深度为 \(2\) 的点都有至少一个儿子)。显然,不能删叶子结点, 也不能保留一棵子树,那就只能一棵棵地删去子树。也能得出和菊花图类似的结论:如果根节点为奇点,先手必败,偶点相反。

所以像菊花图一样,如果能找到个根节点为奇点的这种子树,那么截取下来,先手必胜;否则就不能截取,也不能删这棵子树中任何一个结点(不然别人就可以截取下来获胜)。

继续,若深度为 \(4\),如果树中有上述的这种子树的话(也就是除根结点外所有点都是偶点,当然树的深度是小于等于 \(3\) 的),那么直接截取,否则只能一个个删深度为 \(2\) 的点的子树,同样,如果根节点为奇点,先手必败,偶点相反。

关键来了:我们可以以此类推下去,如果一棵子树的结点全是偶点(下称全偶图),那么我们就不能删其中任何一个结点,也不能截取这种树。如果有一个结点的所有儿子的子树都是全偶图,但这个点是奇点,那么只能一个个删它的儿子,最后先手必败。

得到上述结论后就好办了,如果真的有一个结点的所有儿子的子树都是全偶图,但这个点是奇点的话,那么直接将这个点截取下来,那么你就赢了(可以证明这种点一定存在,除非整个图就是一个全偶图,此时只需要删除一个深度为 \(2\) 的结点的子树即可)。但如果原树的根结点就是这种结点的话,你就输了(就相当于出题人把这种树给了你)。

所以结论就是,如果根结点是奇点,其它点是偶点,那么先手必败,否则必胜。


代码就非常简单了

#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#define ll long long
using namespace std;
const int N = 1e5+5;
bool vis[N],b;
int siz[N],t,n;
vector<int> v[N];
void dfs(int x)
{
	vis[x] = 1;
	for(int i = 0;i < v[x].size();i++)
	{
		int nw = v[x][i];
		if(!vis[nw]){dfs(nw);siz[x]++;}
	}
	if(x!=1&&(siz[x]&1))b = 0;
}
int main()
{
	cin >> t;
	while(t--)
	{
		cin >> n;
		memset(vis,0,sizeof vis);memset(siz,0,sizeof siz);
		b = 1;for(int i = 1;i <= n;i++)v[i].clear();
		for(int i = 1;i < n;i++)
		{
			int x,y;scanf("%d%d",&x,&y);
			v[x].push_back(y);v[y].push_back(x);
		}
		dfs(1);puts(b&&(siz[1]&1)?"Luna":"Hifuu");
	}
	return 0;
}

\(5.16 :\) 突然发现一种简单的写法:如果根结点以外的点是偶点,那么加上父亲就相当于有奇数个点和它相连。那么后手赢当且仅当无根树所有点的度数都为奇数。

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int N = 1e5+5;
int s[N],n,t;
int main()
{
	cin >> t;
	while(t--)
	{
		memset(s,0,sizeof s);
		cin >> n;bool b = 0;
		for(int i = 1;i < n;i++)
		{
			int x,y;cin >> x >> y;
			s[x]++;s[y]++;
		}
		for(int i = 1;i <= n;i++)
			if(!(s[i]&1))b = 1;
		puts(b?"Hifuu":"Luna");
	}
	return 0;
}
posted @ 2024-07-29 19:25  max0810  阅读(17)  评论(2编辑  收藏  举报