AtCoder Beginner Contest 067

A

题意

Snuke 有两个罐头分别有 \(A, B\) 块饼干,有三只山羊。询问是否能使用一个或两个罐头使得山羊们可以均分饼干。

题解

枚举是否 \(A, B, A + B\) 存在 \(3\) 的倍数。

小加强:
题意:
Snuke 有 \(n\) 个罐头,按顺序有 \(a_1, a_2, \cdots, a_n\) 块饼干。询问能否选出连续的一段罐头,使 \(k\) 只山羊可以均分饼干。

解:
\(s_i = \sum_{i = 1}^{i} a_i\)

\(k \leq n\)\(s_1, s_2, \cdots, s_n\)\(\bmod k\) 意义下,由鸽巢原理一定存在 \(s_i = 0\) 或者 \(s_i = s_j\) 。故一定有解。
\(k > n\) ,也只需要查询是否存在 \(s_i\) 或者 \(s_i = s_j\)

B

题意

\(N\) 根木棍,选 \(K(K \leq N)\) 根木棍拼接成,询问能够得到的最长长度。

题解

排序后计算最大的 \(K\) 个数之和。

C

题意

一个牌堆,从上到下分别写着 \(a_1, a_2, \cdots, a_n\) 作为它们的权值。

先拿走顶上一堆,这堆牌的权值和为 \(x\) 。再拿走剩下一堆,这堆牌的权值和为 \(y\) 。两堆牌不能为空。

询问 \(|x - y|\) 最少是多少。

题解

处理 \(s_i = \sum_{i = 1}^{n} a_i\)
枚举 \(i \in [2, n]\) 为第二堆的顶部,由容斥原理第二堆牌权值和为 \(s_{N} - s_{i - 1}\)
第一堆牌权值和为 \(s_{i - 1}\)

维护最小的 \(s_{N} - s_{i - 1} \times 2\) 即可。

view
	int N; std::cin >> N;
	std::vector<i64> s(N + 1);
	for (int i = 1; i <= N; i++) {
		std::cin >> s[i];
		s[i] += s[i - 1];
	}
	i64 ans = 1LL << 60;
	for (int i = 2; i <= N; i++) {
		ans = std::min<i64>(ans, abs(s[N] - s[i - 1] * 2));
	}
	std::cout << ans << "\n";

D

题意

给定一棵 \(n\) 个结点的树,开始时 \(1\) 号点是黑色,\(n\) 号点是白色,其他点没有颜色。

Fennec 先操作,选择一个黑色节点,然后对一个没有颜色的相邻节点染上黑色。

Snuke 后操作,选择一个白色节点,然后对一个没有颜色的相邻节点染上白色。

交替进行。

如果某个人无法操作,则失败。对手获胜。

询问两个人都按最优策略操作的情况下的最终获胜者。

题解

观察 \(1\)\(n\) 的一条路径,一段会被染成黑色,一段会被染成白色。假设两个端点分别为 \(p_1, p_2\)

黑色最终能够染色的区域是以 \(p_1\) 为根的树大小,减去以 \(p_2\) 为根的子树大小。

白色最终能够染色的区域是以 \(p_2\) 为根的树大小,减去以 \(p_1\) 为根的子树大小。

进一步观察到黑色方希望 \(p_1\) 尽可能离 \(1\) 远,白色方希望 \(p_2\) 尽可能离 \(n\) 远。

于是容易猜测最优策列是双方都优先沿着 \(1\)\(n\) 的这条路径染色。

定理:

一棵树中,任意相邻的两个点 \((u, v)\) 可以严格分出两棵树,且这两棵树只能由 \(e(u, v)\) 连接。

推论:

将一棵树染成 \(n\) 个色块,每个色块内连通。则:

  • 每个色块是当前颜色的一个连通块/连通分量/极大连通图。
  • 每个色块是一棵树。
  • 不同色块间有且仅有一条边相连。

注意到:

  • \(p_1, p_2\) 一定能把一棵树分成两棵。
  • \(1\) 一定能 \(bfs\)\(p_1\)
  • \(n\) 一定能 \(bfs\)\(p_2\)

实际上从 \(1, n\) 开始 bfs 染色就能得到答案。

考虑得到的 \(cnt_0\) 为黑色节点数量,\(cnt_1\) 为白色节点数量。

\(cnt_0 \neq cnt_1\) ,显然是多的一方胜利。否则是后手胜利。

view
	int n; std::cin >> n;
	std::vector<std::vector<int> > adj(n + 1);
	for (int i = 1; i < n; i++) {
		int u, v; std::cin >> u >> v;
		adj[u].push_back(v);
		adj[v].push_back(u);
	}

	std::queue<int> que;
	que.push(1);
	que.push(n);

	std::vector<int> col(n + 1, -1);
	int cnt[2] = {0};
	col[1] = 0, col[n] = 1;
	

	while (!que.empty()) {
		int x = que.front();
		que.pop();
		cnt[col[x]]++;
		for (auto y : adj[x]) if (col[y] == -1) {
			col[y] = col[x];
			que.push(y);
		}
	}

	if (cnt[col[1]] > cnt[col[n]]) std::cout << "Fennec\n";
	else std::cout << "Snuke\n";
posted @ 2024-07-05 21:59  03Goose  阅读(42)  评论(0)    收藏  举报