2018 China Collegiate Programming Contest - Guilin Site
A. Array Merge
题意:给你两个数组,你要把它们合并为一个数组,保持原数组的元素顺序不变。合并后为\(c\),求\(\sum_{i=1}^{n+m} c_i \times i\)最小。
贪心想哪个大就放哪个,但这样如果某个数组前面很小,后面非常大,这个做法就是错的。
然后想到按平均值来看,但不能把一整个还没放进去的一起算平均值,因为一起算代表想把它们一起放进去,但我们可能一次只放一段进去。那么我们怎么分段呢?肯定希望让这一段平均值更大,那么我们可以对两个数组分开处理,把他们切成一段一段的子数组,这些子数组平均值从大到小排列,这样就变成了归并排序。
关于具体怎么分段,我们可以先让每个数单独分段,然后去合并相邻的段,如果后面的平均值比当前段大,那么应该合并。这样一直操作到没有可以合并的为止。
点击查看代码
#include <bits/stdc++.h>
using i64 = long long;
void solve() {
int n, m;
std::cin >> n >> m;
std::vector<i64> a(n), b(m);
for (int i = 0; i < n; ++ i) {
std::cin >> a[i];
}
for (int i = 0; i < m; ++ i) {
std::cin >> b[i];
}
auto work = [&](std::vector<i64> & a) -> std::vector<std::tuple<i64, int, int>> {
int n = a.size();
std::vector<std::tuple<i64, int, int>> res;
for (int i = 0; i < n; ++ i) {
res.emplace_back(a[i], i, i);
}
while (res.size() > 1) {
bool flag = false;
std::vector<std::tuple<i64, int, int>> nres;
nres.push_back(res[0]);
for (int i = 1; i < res.size(); ++ i) {
auto & [s1, l1, r1] = nres.back();
auto & [s2, l2, r2] = res[i];
if (s1 * (r2 - l2 + 1) <= s2 * (r1 - l1 + 1)) {
nres.back() = {s1 + s2, l1, r2};
flag = true;
} else {
nres.push_back(res[i]);
}
}
res = nres;
if (!flag) {
break;
}
}
return res;
};
auto A = work(a);
auto B = work(b);
i64 ans = 0, id = 1;
auto get = [&](std::vector<i64> & a, int l, int r) -> void {
for (int i = l; i <= r; ++ i) {
ans += a[i] * id;
++ id;
}
};
for (int i = 0, j = 0; i < A.size() || j < B.size();) {
if (i == A.size()) {
auto & [_, l, r] = B[j];
get(b, l, r);
++ j;
} else if (j == B.size()) {
auto & [_, l, r] = A[i];
get(a, l, r);
++ i;
} else {
auto & [s1, la, ra] = A[i];
auto & [s2, lb, rb] = B[j];
if (s1 * (rb - lb + 1) >= s2 * (ra - la + 1)) {
get(a, la, ra);
++ i;
} else {
get(b, lb, rb);
++ j;
}
}
}
std::cout << ans << "\n";
}
int main() {
std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
int t;
std::cin >> t;
for (int i = 1; i <= t; ++ i) {
std::cout << "Case " << i << ": ";
solve();
}
return 0;
}
D. Bits Reverse
题意:给你两个数\(x, y\),每次你可以把一个数二进制下的连续三位翻转。求\(x\)变成\(y\)的最小操作数。
三位翻转其实中间那一位并没有变。所以是奇数位置和奇数位置交换,偶数位置和偶数位置交换。分奇偶讨论,就变成了两个\(01\)数组,每次交换一个数组两个相邻的位置,求相等的最小操作数。这个之间贪心的按顺序从小到大匹配\(1\)就行了。
点击查看代码
#include <bits/stdc++.h>
using i64 = long long;
void solve() {
i64 x, y;
std::cin >> x >> y;
std::vector<int> x0, x1, y0, y1;
for (int i = 0; i < 60; ++ i) {
if (x >> i & 1) {
if (i & 1) {
x0.push_back(i);
} else {
x1.push_back(i);
}
}
if (y >> i & 1) {
if (i & 1) {
y0.push_back(i);
} else {
y1.push_back(i);
}
}
}
if (x0.size() != y0.size() || x1.size() != y1.size()) {
std::cout << -1 << "\n";
return;
}
int ans = 0;
for (int i = 0; i < x0.size(); ++ i) {
ans += std::abs(x0[i] - y0[i]) / 2;
}
for (int i = 0; i < x1.size(); ++ i) {
ans += std::abs(x1[i] - y1[i]) / 2;
}
std::cout << ans << "\n";
}
int main() {
std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
int t;
std::cin >> t;
for (int i = 1; i <= t; ++ i) {
std::cout << "Case " << i << ": ";
solve();
}
return 0;
}
G. Greatest Common Divisor
题意:给你一个数组,你每次给所有数加一,求使得数组的\(gcd\)大于\(1\)的最小操作数。
先从小到大排序。假设最终数组的\(gcd\)为\(d\),那么因为\(d | a_i, d | d_{i+1}\),所以\(d | a_{i+1} - a_i\)。而\(a_{i+1} - a_i\)不管怎么操作都是不变的,那么\(d\)一定是所有相邻两个数差值的\(gcd\)。枚举这个\(gcd\)的因子取操作数最小的就行。
特殊情况就是所有数都相同,那么是\(1\)就需要加一,否则不需要操作。
点击查看代码
#include <bits/stdc++.h>
using i64 = long long;
void solve() {
int n;
std::cin >> n;
std::vector<i64> a(n);
int d = 0;
for (int i = 0; i < n; ++ i) {
std::cin >> a[i];
d = std::gcd(d, a[i]);
}
if (n == 1) {
std::cout << (a[0] == 1) << "\n";
return;
}
if (d != 1) {
std::cout << 0 << "\n";
return;
}
std::ranges::sort(a);
d = 0;
for (int i = 1; i < n; ++ i) {
d = std::gcd(d, a[i] - a[i - 1]);
}
if (d == 0) {
std::cout << (a[0] == 1) << "\n";
return;
}
if (d == 1) {
std::cout << -1 << "\n";
return;
}
auto get = [&](i64 x, i64 y) -> i64 {
return (x + y - 1) / y * y;
};
i64 ans = 1e18;
for (int i = 2; i <= d / i; ++ i) {
if (d % i == 0) {
ans = std::min(ans, get(a[0], i) - a[0]);
ans = std::min(ans, get(a[0], d / i) - a[0]);
}
}
ans = std::min(ans, get(a[0], d) - a[0]);
std::cout << ans << "\n";
}
int main() {
std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
int t;
std::cin >> t;
for (int i = 1; i <= t; ++ i) {
std::cout << "Case " << i << ": ";
solve();
}
return 0;
}
H. Hamming Distance
题意:两个字符串的\(Hamming\)距离定义为\(a_i \ne b_i\)的数量。给你两个字符串,要求构造一个字典序最小的字符串,使得它和这两个字符串的\(Hamming\)距离相等。
贪心。
首先两个字符串相同的位置不管怎么放都无所谓,因为要么都不加距离,要么都加距离。那么直接放\(a\)就行了。
记下不同的位置的数量,然后从小到大贪心,枚举放哪个字符,讨论他对两个距离的差值的贡献,我们希望距离相等,也就是它们距离的差值为\(0\)。那么如果放了这个字符后,后面可以把差值变成\(0\),那么就可以放,找最小的放就行。
点击查看代码
#include <bits/stdc++.h>
using i64 = long long;
void solve() {
std::string s, t;
std::cin >> s >> t;
int n = s.size();
int cnt = 0;
for (int i = 0; i < n; ++ i) {
cnt += s[i] != t[i];
}
std::string ans;
int diff = 0;
for (int i = 0; i < n; ++ i) {
if (s[i] == t[i]) {
ans += 'a';
} else {
for (char c = 'a'; c <= 'z'; ++ c) {
int ndiff = diff + (s[i] != c) - (t[i] != c);
if (std::abs(ndiff) <= cnt - 1) {
-- cnt;
diff = ndiff;
ans += c;
break;
}
}
}
}
std::cout << ans << "\n";
}
int main() {
std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
int t;
std::cin >> t;
for (int i = 1; i <= t; ++ i) {
std::cout << "Case " << i << ": ";
solve();
}
return 0;
}
J. Stone Game
题意:两个人博弈。有\(n\)堆石头,第\(i\)堆有\(a_i\)个石头。每次从一堆拿走一个,不能出现\(a_i = a_{i+1}\)的情况。保证初始状态合法。求谁是赢家。
记\(a_1 < a_0, a_n < a_{n+1}\)。
那么一定有\(a_i < a_{i-1}\)且\(a_i < a_{i+1}\)的位置。这些位置一定可以拿到只剩\(0\)。那么对于它两边的石堆,剩下的数要大于它,同时也要大于它另一个相邻的数,如果求出另一个相邻数可以剩多少,那么这个数就是两个相邻数剩下最多的数加一。那么可以\(bfs\)从\(a_i < a_{i-1}\)且\(a_i < a_{i+1}\)的位置开始遍历,每次把左右入队,更新答案。那么总共可以拿奇数就是\(Alice\)赢。
点击查看代码
#include <bits/stdc++.h>
using i64 = long long;
void solve() {
int n;
std::cin >> n;
std::vector<int> a(n + 2), b(n + 2);
for (int i = 1; i <= n; ++ i) {
std::cin >> a[i];
}
a[0] = a[n + 1] = 2e9;
std::vector<int> st(n + 2);
st[0] = st[n + 1] = 1;
std::queue<int> q;
for (int i = 1; i <= n; ++ i) {
if (a[i] < a[i - 1] && a[i] < a[i + 1]) {
q.push(i);
st[i] = 1;
}
}
auto check = [&](int x) -> bool {
if (st[x]) {
return false;
}
return (a[x - 1] > a[x] || st[x - 1]) && (a[x + 1] > a[x] || st[x + 1]);
};
while (q.size()) {
int u = q.front(); q.pop();
int l = u - 1, r = u + 1;
if (check(l)) {
q.push(l);
st[l] = 1;
b[l] = std::max(b[l - 1], b[l + 1]) + 1;
}
if (check(r)) {
q.push(r);
st[r] = 1;
b[r] = std::max(b[r - 1], b[r + 1]) + 1;
}
}
i64 sum = 0;
for (int i = 1; i <= n; ++ i) {
sum += a[i] - b[i];
}
if (sum & 1) {
std::cout << "Alice\n";
} else {
std::cout << "Bob\n";
}
}
int main() {
std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
int t;
std::cin >> t;
for (int i = 1; i <= t; ++ i) {
std::cout << "Case " << i << ": ";
solve();
}
return 0;
}

浙公网安备 33010602011771号