Codeforces Gym 102392F Game on a Tree (SEERC2019 F题) 题解

题目链接https://codeforces.com/gym/102392/problem/F

题意:被这题题意坑了很久,大意是说有一棵根为 (1) 的树,每个节点初始都是白色, (Alice) 能在这棵树的某个节点放下一个棋子,并使得该节点变为黑色,然后从 (Bob) 开始,两人能轮流移动这个棋子到当前所在节点的任意一个白色的祖先或者后代节点(不需要相邻的节点),并且将移动到的节点染为黑色。谁先不能移动就输了。

分析:看到这道题,大致就是一个树上的博弈问题。我们一开始读错题意,以为必须得是移动到与当前节点相邻的节点,那这道题就很容易转化为求树上最大匹配的问题。我们发现如果这棵树的最大匹配是完美匹配,那么后手必胜,反之先手必胜,原因是:不论先手选哪一个节点,后手都能将棋子移动到与当前节点匹配的某一节点。但如果不存在完美匹配,那先手必然能避免这种情况。然后敲了个树上最大匹配交了上去就wa32了。。。

最后才发现不需要移动到相邻节点,事实上解决方法也类似上面说的树上最大匹配,只不过这个最大匹配并不需要相邻节点,只要某两个点满足互为祖先节点和后代节点就能匹配,这个问题可以通过树形 (dp) 解决。

我们记 (dp_v)(v) 节点以及该节点对应子树未匹配节点的最小数量, (cnt)(sum{dp_k}) (v节点所有后代节点dp值之和),那么就容易得到:$$egin{cases}
dp_v = cnt - 1 (cnt > 0), dp_v = 1 (cnt = 0).
end{cases}

[如果 $dp[1]$ 的值为$0$ ,说明存在完美匹配。 **AC代码**: ``` #include <bits/stdc++.h> using namespace std; void io() { ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr); } int n, x, y, dp[SIZE]; bool vis[SIZE]; vector<int> G[SIZE]; void dfs(int fa, int now) { bool f = false; dp[now] = -1; for (auto i : G[now]) { if (i == fa) continue; dfs(now, i); if (vis[i]) f = true; dp[now] += max(dp[i], (vis[i] ? 1 : 0)); } if (!f || dp[now] > 0) vis[now] = true; } int main() { io(); cin >> n; rep(i, 1, (n - 1)) { cin >> x >> y; G[x].emplace_back(y); G[y].emplace_back(x); } dfs(0, 1); if (dp[1]) puts("Alice"); else puts("Bob"); } ```]

原文地址:https://www.cnblogs.com/st1vdy/p/11789546.html