Educational Codeforces Round 108 (Rated for Div. 2)【ABCD】
五一快乐 (~ ̄▽ ̄)~
比赛链接:https://codeforces.com/contest/1519
A. Red and Blue Beans
题意
有 \(r\) 个红色豆子和 \(b\) 个蓝色豆子,需要将它们分成几包,要求每包:
- 至少有一个红色豆子
- 至少有一个蓝色豆子
- 两种颜色豆子的数量之差不超过 \(d\)
问能否将所有豆子分完 ?
题解
假设红色豆子比蓝色豆子少,为了尽可能地将蓝色豆子分完,每包应该放进 \(1\) 个红色豆子, \(1 + d\) 个蓝色豆子,否则无解。
代码
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t;
cin >> t;
while (t--) {
int r, b, d;
cin >> r >> b >> d;
if (r > b) {
swap(r, b);
}
cout << (1LL * r * (d + 1) >= b ? "YES" : "NO") << "\n";
}
return 0;
}
B. The Cake Is a Lie
题意
有一个 \(n \times m\) 的方阵,假设当前在 \((x, y)\) 点,可以从以下两种移动操作中选择一种:
- 向右移动到 \((x, y + 1)\) ,花费为 \(x\)
- 向下移动到 \((x + 1, y)\) ,花费为 \(y\)
起点为 \((1, 1)\) ,问抵达终点 \((n, m)\) 的花费能否刚好为 \(k\) 。
题解
花费只与起点与终点有关,与路径无关,即只需判断 \(k\) 是否等于从 \((1, 1)\) 到 \((n, m)\) 的花费即可。
证明
假设有一个 \(2 \times 2\) 的方阵,从左上角 \((x, y)\) 到右下角 \((x + 1, y + 1)\) 显然只有两条路径,易得两条路径的花费均为 \(x + y + 1\) 。
即,在一个 \(2 \times 2\) 方阵中,路径是可以关于主对角线翻折的。
那么对于任意起点到任意终点间的任意路径,都可以利用路径中折角所在的 \(2 \times 2\) 方阵将路径翻折至同一种。
从 \((1, 1)\) 到 \((n, m)\) 最简单的路径即直右直下(或直下直右),花费即 \((m - 1) \times 1 + (n - 1) \times m = n \times m - 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, m, k;
cin >> n >> m >> k;
cout << (k == n * m - 1 ? "YES" : "NO") << "\n";
}
return 0;
}
C. Berland Regional
题意
有 \(n\) 个学生,他们分别来自大学 \(u_i\) 并且编程能力为 \(s_i\) 。
当队伍人数为 \(k\) 时,每个大学会以 \(s_i\) 为依据从大到小成 \(k\) 个将学生尽可能多地组队派出。
问当 \(k\) 的值为 \(1,2,...,n\) 时,每次对应的所有队伍的编程能力之和为多少。
题解
对于学生人数为 \(size\) 的大学,当 \(k > size\) 时无法再派出队伍,所以该大学只需考虑 \(k \in [1, size]\) 的情况。
当 \(k\) 为某个值时,该大学的后 \(size\ \%\ k\) 个学生无法组队派出,即 \(ans[k] \mathrel{+}= s_0 + s_1 + \dots + s_{size - 1 - (size\ \%\ 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;
cin >> n;
vector<int> u(n);
for (int i = 0; i < n; i++) {
cin >> u[i];
--u[i];
}
vector<int> s(n);
for (int i = 0; i < n; i++) {
cin >> s[i];
}
vector<vector<long long>> a(n);
for (int i = 0; i < n; i++) {
a[u[i]].push_back(s[i]);
}
vector<long long> ans(n + 1);
for (auto vec : a) {
sort(vec.begin(), vec.end(), greater<>());
for (int i = 1; i < (int)vec.size(); i++) {
vec[i] += vec[i - 1];
}
for (int k = 1; k <= (int)vec.size(); k++) {
ans[k] += vec[(int)vec.size() - 1 - (int)vec.size() % k];
}
}
for (int i = 1; i <= n; i++) {
cout << ans[i] << " \n"[i == n];
}
}
return 0;
}
D. Maximum Sum of Products
题意
给出两个大小为 \(n\) 的数组 \(a、b\) ,最多可以反转一次 \(a\) 中的连续子数组,问 \(\sum_{i = 1}^{n} \limits a_i \cdot b_i\) 的最大值。
题解
枚举中心点,依次向两边扩展。
Tips
注意连续子数组长度分奇偶两种情况。
代码
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
cin >> n;
vector<long long> a(n);
for (int i = 0; i < n; i++) {
cin >> a[i];
}
vector<long long> b(n);
for (int i = 0; i < n; i++) {
cin >> b[i];
}
long long ans = 0;
for (int i = 0; i < n; i++) {
ans += a[i] * b[i];
}
long long max_delta = 0;
for (int i = 0; i < n; i++) {
long long delta = 0;
for (int l = i - 1, r = i + 1; l >= 0 and r < n; --l, ++r) {
delta -= a[l] * b[l] + a[r] * b[r];
delta += a[l] * b[r] + a[r] * b[l];
max_delta = max(max_delta, delta);
}
delta = 0;
for (int l = i, r = i + 1; l >= 0 and r < n; --l, ++r) {
delta -= a[l] * b[l] + a[r] * b[r];
delta += a[l] * b[r] + a[r] * b[l];
max_delta = max(max_delta, delta);
}
}
cout << ans + max_delta << "\n";
return 0;
}