SEERC 2019 F. Game on a Tree

题目大意:

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

思路:

  做的时候一直以为他的跳转是只能跳转到当前点的相邻结点,但其实这道题可以跳到任意祖先结点或者后代结点,其实不管是上面说的哪种情况都可以同样用判断图是否存在完美匹配解得。

两种情况:
  1. 存在完美匹配:那么不管Alice从哪个点起步,Bob都可以找到该点的匹配点并染色,这样下来最先无法染色的一定是Alice。
  2. 不存在完美匹配:那么Alice可以选择不在最大匹配中的点起步,假设起始点为a1,Bob走的下一步为b1,b1其实必在最大匹配中,因为如果不在,那么(a,b) 又构成一个匹配,与最大匹配矛盾。那么接下来Alice就可以采取第一种情况中Bob的操作,因为Bob接下来是无法到达最大取匹配外的点的(假设b3在最大匹配外,那么匹配集(a1,b1),(a2,b2),(a3,b3)…的匹配数一定大于最大匹配(b1,a2),(b2,a3)…(后边都一样))所以Alice只要走最大匹配外的点,接下来Bob只能走最大匹配内的点,类似于第一种情况只不过Bob先手,所以Alice必赢。

code:

#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <iomanip>
#include <string>
#include <set>
#include <vector>
#include <queue>
#include <stack>
#include <map>
using namespace std;
#define ms(a, x) memset(a, x, sizeof(a))
#define fore(i, a, n) for (ll ll i = a; i < n; i++)
#define ford(i, a, n) for (ll ll i = n - 1; i >= a; i--)
#define si(a) scanf("%d", &a)
#define sl(a) scanf("%lld", &a)
#define sii(a, b) scanf("%d%d", &a, &b)
#define siii(a, b, c) scanf("%d%d%d", &a, &b, &c)
#define sll(a, b) scanf("%lld%lld", &a, &b)
#define slll(a, b, c) scanf("%lld%lld%lld", &a, &b, &c)
#define debug(a) cout << a << endl
#define pr(a) printf("%d ", a)
#define endl '\n'
#define pi acos(-1.0)
#define tr t[root]
#define IO ios::sync_with_stdio(false), cin.tie(0)
#define ull unsigned long long
#define py puts("Yes")
#define pn puts("No")
#define pY puts("YES")
#define pN puts("NO")
#define re(i, a, b) for (int i = a; i <= b; ++i)
#define de(i, a, b) for (int i = a; i >= b; --i)
#define all(x) (x).begin(), (x).end()
#define ls(x) x.resize(unique(all(x)) - x.begin())
const double eps = 1e-8;
inline int sgn(const double &x) { return x < -eps ? -1 : x > eps; }
typedef long long ll;
const ll inf = 0x3f3f3f3f;
const int MAXN = 1e5 + 5;
template <class T>
inline void cmin(T &a, T b) { ((a > b) && (a = b)); }
template <class T>
inline void cmax(T &a, T b) { ((a < b) && (a = b)); }
const ll mode = 20090126;
//上边只是头

int f[MAXN];
vector<int> edge[MAXN];

void dfs(int x, int fa)
{
	for (auto to : edge[x])
	{
		if (to == fa)
			continue;
		dfs(to, x);
		f[x] += f[to];
	}
	if (f[x])
		f[x]--;
	else
		f[x] = 1;
}

int main()
{
	int n;
	si(n);
	for (int i = 1; i < n; i++)
	{
		int u, v;
		sii(u, v);
		edge[u].push_back(v);
		edge[v].push_back(u);
	}
	dfs(1, 0);
	if (!f[1])
		printf("Bob\n");
	else
		printf("Alice\n");
}

代码中的dfs过程是因为这道题不要求跳转的点一定是相邻的,和真实情况的图的匹配不太一样,只要两个点互为祖先和后代结点,这里就视为可以匹配。

实际上判断图是否存在完美匹配可以参考:https://blog.csdn.net/lycheng1215/article/details/78368002

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值