海亮01/04博弈论杂题

发布时间 2024-01-04 08:24:19作者: Call_me_Eric

海亮01/04博弈论杂题

T1

AT_agc017_d

题意

有一棵 \(N\) 个节点的树,节点标号为 \(1,2,⋯,N\),边用 \((x_i,y_i)\)表示。 Alice 和 Bob 在这棵树上玩一个游戏,Alice先手,两人轮流操作:

选择一条树上存在的边,把它断开使树变成两个连通块。然后把不包含 \(1\) 号点的联通块删除

当一个玩家不能操作时输,你需要算出:假如两人都按最优策略操作,谁将获胜。

题解

先考虑如果根只有一个子树怎么办?

显然一定是先手胜对叭?

如果是两个呢?

算一下两个子树的 SG 函数值,然后分成两个子游戏即可,最后异或一下就可以了。

但是三个呢?多个子树(\(k\) 个)呢?

发现我们可以将根克隆出 \(k\) 个出来,每一个都连接一个子树。然后发现这个东西将原来的游戏分成了 \(k\) 个子游戏,每个游戏的 SG 都是直接算出子树的 SG \(+1\) 得到的(加了一条边连向父亲对吧)。

然后就没了。

代码

#include<bits/stdc++.h>
using namespace std;
inline int read(){
	int x = 0,f = 1;char ch = getchar();
	while(ch < '0' || ch > '9'){if(ch == '-') f = -1;ch = getchar();}
	while(ch >= '0' && ch <= '9'){x = (x << 1) + (x << 3) + (ch ^ 48);ch = getchar();}
	return x * f;
}
const int maxn = 2e5 + 10;
vector<int> edg[maxn];
int n;
int dfs(int u,int f){
	int res = 0;
	for(int v : edg[u])if(v != f)res ^= (dfs(v, u) + 1);
	return res;
}
signed main(){
	n = read();int u, v;
	for(int i = 1;i < n;i++){
		u = read(); v = read();
		edg[u].push_back(v);edg[v].push_back(u);
	}
	puts(dfs(1, 0) ? "Alice" : "Bob");
	return 0;
}