比赛链接:https://codeforces.com/contest/1405
A. Permutation Forgery
题意
给出一个大小为 $n$ 的排列 $p$,定义
egin{equation} F(p)=mathrm{sort}([p_1+p_2,p_2+p_3,ldots,p_{n-1}+p_n]) 。 onumber end{equation}
试找出一个不同于 $p$ 的 $p'$ 满足 $F(p') = F(p)$ 。
题解
反转 $p$ 即可。
代码
#include <bits/stdc++.h> using namespace std; int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int t; cin >> t; while (t--) { int n; cin >> n; vector<int> a(n); for (int i = 0; i < n; i++) cin >> a[i]; reverse(a.begin(), a.end()); for (int i = 0; i < a.size(); i++) cout << a[i] << " "[i == a.size() - 1]; } return 0; }
B. Array Cancellation
题意
给出一个大小为 $n$,满足 $sum_{i=0}^{n-1} a_i = 0$ 的数组 $a$,每次可选择的操作如下:
- 选择 $i < j$,将 $a_i$ 减一,$a_j$ 加一,花费为 $0$
- 选择 $i > j$,将 $a_i$ 减一,$a_j$ 加一,花费为 $1$
问将 $a_i$ 均变为 $0$ 的最小花费是多少。
题解一
分别存储正数和负数的位置,然后贪心加双指针模拟。
代码
#include <bits/stdc++.h.> using namespace std; int main() { ios::sync_with_stdio(false); cin.tie(0); int t; cin >> t; while (t--) { int n; cin >> n; vector<int> a(n); vector<int> posi, nega; for (int i = 0; i < n; i++) { cin >> a[i]; if (a[i] > 0) posi.push_back(i); if (a[i] < 0) nega.push_back(i); } int j = 0; for (int i = 0; i < int(posi.size()); i++) { while (a[posi[i]] > 0) { while (j < int(nega.size()) and nega[j] < posi[i]) ++j; if (j == int(nega.size())) break; int mi = min(a[posi[i]], -a[nega[j]]); a[posi[i]] -= mi; a[nega[j]] += mi; if (a[nega[j]] == 0) ++j; } } long long ans = 0; for (int i = 0; i < n; i++) if (a[i] > 0) ans += a[i]; cout << ans << " "; } return 0; }
题解二
最小负前缀的绝对值即为最少花费。
证明
对于最小负前缀,内部操作不会影响该负前缀的总和,涉及外部操作的最佳方案就是对负前缀加一,因为 $sum_{i=0}^{n-1} a_i = 0$,所以此时后缀为正且绝对值与该负前缀相等,将二者各视为一个整体操作即可。
代码
#include <bits/stdc++.h> using namespace std; int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int t; cin >> t; while (t--) { int n; cin >> n; long long ans = 0, cur = 0; for (int i = 0; i < n; i++) { int x; cin >> x; cur += x; ans = min(ans, cur); } cout << -ans << " "; } return 0; }
C. Balanced Bitstring
题意
如果一个偶数长 $k$ 的二进制串有 $frac{k}{2}$ 个 $0$ 和 $1$,那么称这个二进制串为 $k$ 平衡串。给出一个由 $0,1,?$ 组成的字符串 $s$,$s$ 中的 $?$ 可被替换为 $0$ 或 $1$,判断 $s$ 是否可能为 $k$ 平衡串。
题解
因为共享中间的 $k-1$ 个字符,所以 $s_i = s_{i+k}$ 。
所以如果第一个 $k$ 长子串确定合法,则整个字符串确定合法。
另外还需枚举 $k$ 个起点并判断一开始的字符串中步长为 $k$ 的路径上的所有字符是否相同。
代码
#include <bits/stdc++.h> using namespace std; int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int t; cin >> t; while (t--) { int n, k; cin >> n >> k; string s; cin >> s; bool ok = true; int zero = 0, one = 0; for (int i = 0; i < k; i++) { bool a0 = false, a1 = false; for (int j = i; j < n; j += k) if (s[j] != '?') (s[j] == '0' ? a0 : a1) = true; if (a0 and a1) ok = false; else if (a0 or a1) ++(a0 ? zero : one); } if (max(zero, one) > k / 2) ok = false; cout << (ok ? "YES" : "NO") << " "; } return 0; }
D. Tree Tag
题意
给出一颗树,Alice在结点 $a$,每次可以走的距离最多为 $da$,Bob在结点 $b$,每次可以走的距离最多为 $db$,Alice先走,问在无限步内Alice能否走到Bob的位置。
题解
Alice获胜的三种情况:
- 一开始Bob在Alice的覆盖半径内,因为Alice先手,所以一步就可以走到Bob的位置
- Alice的覆盖直径大于等于树的直径,此时Alice可以移到树的中心并覆盖树上的每一个点
- Alice的覆盖直径大于等于Bob的步长,此时以Alice的起点为根节点,Bob无法超出Alice的覆盖范围移到另一颗子树
代码
#include <bits/stdc++.h> using namespace std; int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int t; cin >> t; while (t--) { int n, a, b, da, db; cin >> n >> a >> b >> da >> db; --a, --b; vector<vector<int>> G(n); for (int i = 0; i < n - 1; i++) { int u, v; cin >> u >> v; --u, --v; G[u].push_back(v); G[v].push_back(u); } int diam = 0; vector<int> dep(n); function<int(int, int)> dfs = [&](int u, int p) { int len = 0; for (auto v : G[u]) { if (v != p) { dep[v] = dep[u] + 1; int cur = 1 + dfs(v, u); diam = max(diam, cur + len); len = max(len, cur); } } return len; }; dfs(a, -1); cout << (2 * da >= min(diam, db) or dep[b] <= da ? "Alice" : "Bob") << " "; } return 0; }