Educational Codeforces Round 76 (Rated for Div. 2)
比赛链接:https://codeforces.com/contest/1257
A - Two Rival Students
题意
有 $n$ 个学生站成一排,最多使相邻两个学生交换位置 $k$ 次,给出两个学生的位置,计算这两个学生能相距的最远距离。
代码
#include <bits/stdc++.h> using namespace std; void solve() { int n, x, a, b; cin >> n >> x >> a >> b; cout << min(n - 1, abs(b - a) + x) << "\n"; } int main() { int t; cin >> t; while (t--) solve(); }
B - Magic Stick
题意
给出正整数 $x,y$,对 $x$ 可以选择执行两种操作:
- 如果 $x$ 为偶数,将 $x$ 变为 $\frac{3x}{2}$
- 如果 $x>1$,将 $x$ 变为 $x - 1$
判断 $x$ 能否变为 $y$ 。
代码
#include <bits/stdc++.h> using namespace std; void solve() { int x, y; cin >> x >> y; if (x >= y or (x == 2 and y == 3)) cout << "YES" << "\n"; else cout << (x <= 3 ? "NO" : "YES") << "\n"; } int main() { int t; cin >> t; while (t--) solve(); }
C - Dominated Subarray
题意
找出一个数组大小至少为 $2$ 的最小连续子数组,要求该子数组中出现次数最多的值只有一个。
题解
依次计算每个值相邻两个元素的最短距离即可。
证明
如果一个值的相邻两个元素之间有出现两次的其他值,说明其他值相邻两个元素的距离更短,最终也一定会被取到。
代码
#include <bits/stdc++.h> using namespace std; void solve() { int n; cin >> n; vector<int> pos[n + 1]; for (int i = 0; i < n; i++) { int x; cin >> x; pos[x].push_back(i); } int ans = INT_MAX; for (auto v : pos) { for (int i = 1; i < v.size(); i++) { ans = min(ans, v[i] - v[i - 1] + 1); } } cout << (ans == INT_MAX ? -1 : ans) << "\n"; } int main() { int t; cin >> t; while (t--) solve(); }
D - Yet Another Monster Killing Problem
题意
有 $n$ 个怪物在山洞中排成一排,每个怪物的力量为 $a_i$,你有 $m$ 个战士,每个战士的力量为 $p_i$,只有当战士的力量不低于怪物时才可以击败怪物,每个战士一天最多击败 $s_i$ 只怪物。一天只能选派一个战士,计算击败全部怪物所需的最少天数。(怪物被击败后不会复活)
题解
预处理一遍得到耐久度至少为 $i$ 的战士的最大力量 $mx_i$,之后利用双指针模拟,每天尽可能多地击败怪物即可。
代码
#include <bits/stdc++.h> using namespace std; void solve() { int n; cin >> n; int a[n] = {}; for (int i = 0; i < n; i++) { cin >> a[i]; } int m; cin >> m; int mx[n + 1] = {}; for (int i = 0; i < m; i++) { int p, m; cin >> p >> m; mx[m] = max(mx[m], p); } for (int i = n - 1; i >= 0; i--) { mx[i] = max(mx[i], mx[i + 1]); } int ans = 0; for (int i = 0; i < n; ) { int j = i, maxn = a[i]; while (j < n and mx[j - i + 1] >= maxn) { ++j; maxn = max(maxn, a[j]); } if (j == i) { ans = -1; break; } else { ++ans; i = j; } } cout << ans << "\n"; } int main() { int t; cin >> t; while (t--) solve(); }
E - The Contest
题意
三个人各持有一个排列的子序列,要求使第一个人持有排列的前一部分,第二个人持有排列的中间一部分,第三个人持有排列的后一部分,三人间至少要移动多少个元素。(可以移动一个人的全部元素)
题解
参考自:qscqesze
逆向考虑,将每个人持有的元素排序后合并为一个序列,最多不需移动的元素个数即为该序列的最长上升子序列。
代码
#include <bits/stdc++.h> using namespace std; const int N = 2e5 + 100; int k1, k2, k3, n; int a[N], b[N], dp[N]; int lis() { int len = 1; b[1] = a[0]; for (int i = 1; i < n; i++) { b[len + 1] = n + 1; int l = 0, r = len + 1, ans = 0; while (l <= r) { int mid = (l + r) / 2; if (a[i] < b[mid]) { ans = mid; r = mid - 1; } else { l = mid + 1; } } b[ans] = a[i]; if (ans > len) ++len; } return n - len; } int main() { cin >> k1 >> k2 >> k3; n = k1 + k2 + k3; for (int i = 0; i < n; i++) { cin >> a[i]; } sort(a, a + k1); sort(a + k1, a + k1 + k2); sort(a + k1 + k2, a + k1 + k2 + k3); cout << lis() << "\n"; }