比赛链接:https://codeforces.com/contest/1408
A. Circle Coloring
题意
给出三个长为 $n$ 的序列 $a,b,c$,对于每个 $i$,$a_i e b_i, a_i e c_i, b_i e c_i$ 。
构造序列 $p$,使得:
- $p_i in {a_i, b_i, c_i}$
- $p_i eq p_{(i + 1 mod n)}$
题解
即每个数不与前后两个数相同,因为三个序列同一位置两两不同,所以对于每个位置一定都有满足条件的数,枚举即可。
代码
#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<vector<int>> vec(3, vector<int> (n)); for (auto &v : vec) { for (auto &x : v) { cin >> x; } } vector<int> p(n); for (int i = 0; i < n; i++) { for (auto &v : vec) { if (v[i] != p[(i - 1 + n) % n] and v[i] != p[(i + 1) % n]) { p[i] = v[i]; break; } } } for (int i = 0; i < n; i++) { cout << p[i] << " "[i == n - 1]; } } return 0; }
B. Arrays Sum
题意
给出一个大小为 $n$ 的非递减序数组 $a$ 和一个正整数 $k$ 。
构造 $m$ 个非递减序数组 $b_1, b_2, ldots, b_m$,要求:
- 每个 $b_i$ 大小为 $n$
- $a_i = b_{1, i} + b_{2, i} + ldots + b_{m, i}$
- 每个 $b_i$ 中最多有 $k$ 个不同的数
找出 $m$ 的最小值。
题解
设 $a$ 中有 $x$ 个不同的数,答案即 $1 + lceil frac{x - k}{k - 1} ceil$ 。
因为 $b_1$ 可以包含 $a$ 中前 $k$ 个不同的数,之后的 $b_i$ 因为要用 $0$ 维护前面已有的和,每次只能新加 $a$ 中 $k-1$ 个不同的数。
代码
#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; set<int> st; for (int i = 0; i < n; i++) { int x; cin >> x; st.insert(x); } if (k == 1) { cout << (st.size() == 1 ? 1 : -1) << " "; continue; } cout << 1 + max(0, (int(st.size()) - 2)) / (k - 1) << " "; } return 0; }
C. Discrete Acceleration
题意
有一条公路从坐标 $0$ 到坐标 $l$ 长为 $l$ 米,初始时甲车在坐标 $0$ 处,乙车在坐标 $l$ 处。
公路上有 $n$ 个加速点,两车初速度均为 $1m/s$,经过一个加速点增加 $1m/s$,计算两车多久相遇。
题解
计算两车到每个加速点的时间,两车会在甲更快到的加速点和乙更快到的加速点之间相遇。
代码
#include <bits/stdc++.h> using namespace std; int main() { ios::sync_with_stdio(false); cin.tie(nullptr); cout << fixed << setprecision(15); int t; cin >> t; while (t--) { int n, l; cin >> n >> l; vector<double> a(n + 2); a[0] = 0; a[n + 1] = l; for (int i = 1; i <= n; i++) { cin >> a[i]; } vector<double> t1(n + 2), t2(n + 2); t1[0] = 0; t2[n + 1] = 0; for (int i = 1; i < n + 2; i++) { t1[i] = t1[i - 1] + (a[i] - a[i - 1]) / i; } for (int i = n; i >= 0; i--) { t2[i] = t2[i + 1] + (a[i + 1] - a[i]) / (n + 1 - i); } double ans = 0.0; for (int i = 1; i < n + 2; i++) { if (t1[i - 1] <= t2[i - 1] and t1[i] >= t2[i]) { ans = max(t1[i - 1], t2[i]) + (a[i] - a[i - 1] - abs(t1[i - 1] - t2[i]) * (t1[i - 1] < t2[i] ? i : n + 2 - i)) / (n + 2); break; } } cout << ans << " "; } return 0; }
D. Searchlights
题意
在二维平面上给出 $n$ 个人和 $m$ 个探照灯的坐标 $(a,b)$ 和 $(c,d)$,每次操作可以选择:
- 将所有人横坐标加一
- 将所有人纵坐标加一
问使得任一人都不在任一探照灯左下方的最少操作次数。
题解
对于第 $i$ 个人和第 $j$ 个探照灯 ,如果 $dx + a_i ge c_j + 1$,那么他一定可以移出探照灯的范围,否则,对于所有小于 $dx = c_j - a_i + 1$ 的移动次数,它们对应的纵坐标移动次数至少要为 $d_j - b_i + 1$ 次才能移出该探照灯的范围。
对于每个人枚举 $m$ 个探照灯,记录并更新每个横坐标移动次数对应的纵坐标移动次数的最大值,最后从后向前遍历并更新纵坐标的最大移动次数,因为如果之前移动了更多的横坐标仍需将纵坐标移动这么多次,那么对于现在移动了更少的横坐标也是必须的。
$dp_i$ 的含义为横坐标移动次数为 $i$ 时,纵坐标的移动次数至少要为 $dp_i$ 才能保证所有人都移出探照灯的范围。
代码
#include <bits/stdc++.h> using namespace std; int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int n, m; cin >> n >> m; vector<int> a(n), b(n); for (int i = 0; i < n; i++) { cin >> a[i] >> b[i]; } vector<int> c(m), d(m); for (int i = 0; i < m; i++) { cin >> c[i] >> d[i]; } constexpr int N = 1e6 + 10; vector<int> dp(N); for (int i = 0; i < n; i++) { for (int j = 0; j < m; j++) { if (a[i] <= c[j]) { dp[c[j] - a[i]] = max(dp[c[j] - a[i]], d[j] + 1 - b[i]); } } } for (int i = N - 2; i >= 0; i--) { dp[i] = max(dp[i], dp[i + 1]); } int ans = INT_MAX; for (int i = 0; i < N; i++) { ans = min(ans, i + dp[i]); } cout << ans << " "; return 0; }
E. Avoid Rainbow Cycles
题意
给出 $m$ 个集合 $A$,所有元素大小在 $[1,n]$ 之间。
每个集合中的元素两两成边,颜色与集合相同,不同集合颜色不同。
给出大小为 $m,n$ 的两个数组 $a,b$,从 $A_i$ 中移除值为 $j$ 的元素花费为 $a_i + b_j$ 。
彩虹环指边颜色不重复的环,问使得图中无彩虹环的最少花费。
题解
构造集合和数值的二分图,$A_i$ 与值为 $j$ 的元素间边权为 $a_i + b_j$,最少花费即为总边权和减去二分图的最大生成树。
代码
#include <bits/stdc++.h> using namespace std; constexpr int N = 2e5 + 10; int fa[N]; int Find(int x) { return fa[x] == x ? x : fa[x] = Find(fa[x]); } void Union(int x, int y) { x = Find(x); y = Find(y); if (x != y) { if (x < y) fa[y] = x; else fa[x] = y; } } void Init() { for (int i = 0; i < N; i++) { fa[i] = i; } } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); Init(); int m, n; cin >> m >> n; vector<int> a(m); for (auto &x : a) cin >> x; vector<int> b(n); for (auto &x : b) cin >> x; long long sum = 0; vector<tuple<int, int, int>> edges; for (int i = 0; i < m; i++) { int s; cin >> s; for (int j = 0; j < s; j++) { int x; cin >> x; --x; sum += a[i] + b[x]; edges.emplace_back(a[i] + b[x], i + n, x); } } sort(edges.begin(), edges.end(), greater<>()); for (auto [w, u, v] : edges) { if (Find(u) != Find(v)) { Union(u, v); sum -= w; } } cout << sum << " "; return 0; }
F. Two Different
题意
给出一个大小为 $n$ 的数组 $a$,其中 $a_i = i$ 。
每次操作可以选取两个元素并用二元映射函数 $f$ 赋值:$a_i = a_j = f(a_i,a_j)$ 。
试给出一种操作方式,使得无论映射函数如何,最终 $a$ 中最多只有两个不同的数。
题解
如果数组大小为 $2^p$,那么最终可以都变为一个数。
如:
- 1 2 3 4
- 5 5 6 6
- 7 7 7 7
如果数组大小不为 $2^p$,对两个端点所在的 $2^p$ 长区间各操作一次即可。
代码
#include <bits/stdc++.h> using namespace std; int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int n; cin >> n; vector<pair<int, int>> ans; function<void(int, int)> dfs = [&](int l, int r) { if (l == r) return; int mid = (l + r) / 2; dfs(l, mid); dfs(mid + 1, r); for (int i = l, j = mid + 1; i <= mid; i++, j++) { ans.emplace_back(i, j); } }; int p = 1 << __lg(n); dfs(1, p); if (p != n) { dfs(n - p + 1, n); } cout << ans.size() << " "; for (auto [x, y] : ans) { cout << x << ' ' << y << " "; } return 0; }
参考博客
https://blog.csdn.net/qq_45458915/article/details/108912813