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";
浙公网安备 33010602011771号