Codeforces Round #768 (Div. 2) 题解
D F未补
A. Min Max Swap
题目描述:给你两个长度为\(n\)的正整数数组\(a ,b\),你可以执行以下操作任意次:
- 找到一个下标\(i\)
- 交换\(a_i\)与\(b_i\)的值
求执行操作后的数组\(a\)的最大值与数组\(b\)的最大值的乘积的最小值。
思路:将所有\(a_i > b _i\)的位置的两元素交换位置,然后分别求两数组的最大值再相乘即可得乘积最小。
时间复杂度:\(O(n)\)
参考代码:
void solve() {
int n;
cin >> n;
vector<int>a(n + 1, 0), b(n + 1, 0);
for (int i = 1; i <= n; ++i) cin >> a[i];
for (int i = 1; i <= n; ++i) cin >> b[i];
for (int i = 1; i <= n; ++i) if (a[i] > b[i]) swap(a[i], b[i]);
int res = (*max_element(a.begin() + 1, a.end())) * (*max_element(b.begin() + 1, b.end()));
cout << res << '\n';
return;
}
B. Fun with Even Subarrays
题目描述:给你一个长度为\(n\)的数组\(a\),每次一个选择一个左边界\(lr\)和一个长度\(k\),满足\(lr + 2 *k - 1 \leq n\)。然后将\(a_{lr + k + i}\)的值赋值给\(a_{lr + i} ,0 \leq i < k\)。问将数组所有元素变相等的最小操作次数。
思路:显然最终数组的所有元素的值都等于\(a_n\),故按照\(a_n\)进行贪心即可。为了好操作,将数组翻转一下从前往后遍历贪心。
时间复杂度:\(O(n)\)
参考代码:
void solve() {
int n;
cin >> n;
vector<int>a(n + 1, 0);
int cnt = 0, res = 0;
for (int i = 1; i <= n; ++i) cin >> a[i];
reverse(a.begin() + 1, a.end());
for (int i = 1; i <= n; ++i) {
if (i <= cnt) continue;
if (a[i] == a[1]) {
++cnt;
continue;
}
++res;
cnt <<= 1;
}
cout << res << '\n';
return;
}
C. And Matching
题目描述:给你一个长度为\(n\)的排列\(0 \sim n - 1\),保证\(n = 2^i (\exists0 \leq i < \infin)\)。将这个排列中的数分成\(\frac{n}{2}\)组,再给定正整数\(k\),假定每一组的元素由\((a , b)\)表示,若存在一种分组方式使得等式:
成立,则输出这种分组方式,否则输出\(-1\)。
思路:注意到\(i \&(n - i - 1) = 0\),那么若\(k \neq n - 1\),那么可以将\(k\)和\(0\)交换位置让\(k\)与\(n - 1\)组成一对,就可以满足题设。当\(k = n - 1\)时,我们可以这样构造,让$ n - 1\(和\)n - 2$为一组,那么还差\(1\),我们可以让\(1\)和\(3\)为一组,然后让\(0\)和$ n - 4$一组,\(2\)和\(n - 3\)一组剩下的还是按照\(i\)与$ n - i - 1\(一组,那么就可以使得分组后满足题设,注意到\)n = 4,k = 3$时不能这样划分,需要特判。
时间复杂度:\(O(n)\)
参考代码:
void solve() {
int n, k;
cin >> n >> k;
if (k == n - 1) {
if (n == 4) {
cout << -1 << '\n';
return;
}
--n;
cout << n - 1 << ' ' << n << '\n';
cout << 0 << ' ' << n - 3 << '\n';
cout << 1 << ' ' << 3 << '\n';
cout << 2 << ' ' << n - 2 << '\n';
for (int i = 4; i < (n + 1) / 2; ++i) cout << i << ' ' << n - i << '\n';
return;
}
vector<vector<int>>res;
int idx = 0;
for (int i = 0; i < n / 2; ++i) {
res.push_back({ i , n - i - 1 });
if (i == k) idx = res.size() - 1;
if (n - i - 1 == k) idx = res.size() - 1;
}
res[0][0] = k;
if (res[idx][0] == k) res[idx][0] = 0;
else res[idx][1] = 0;
for (auto re : res) cout << re[0] << ' ' << re[1] << '\n';
return;
}
E. Paint the Middle
题目描述:给你两个长度为\(n\)的序列\(a\)和\(c\),初始时\(c_i = 0\;\forall 1 \leq i \leq n\)。你可以执行以下操作任意次:
- 选择三个下标\(i , j , k(1 \leq i < j < k \leq n)\)
- \(a_i = a_k\)
- \(c_i = c_j = c_k = 0\)
- 将\(c_j\)赋值成\(1\)
求\(\sum\limits_{i = 1}^{n}c_i\)的最大值。
数据范围:\(3 \leq n \leq 2\times 10^5 , 1 \leq a_i \leq n\)
思路:我们可以在读入的时候使用last
数组记录一下每个数字出现的最后的下标,考虑从前往后遍历,如果当前枚举的元素\(a_i\)最后一次的出现位置为\(j\),则说明区间\((i , j)\)内的值都可以取得,如果\((i , j)\)存在\(k\),使得\(a_{last[k]} > j\),那么\((j , k)\)之间的元素也都可以取得,那么为了让取得的区间最大,就需要找到下标最大的位置,所以我们在遍历\((i , j)\)之间的元素的时候,应该记录一下其对应元素最后一次出现的的位置的最大值,也即
在当前区间被枚举完的时候,使用该值去更新新的区间。
时间复杂度:\(O(n)\)
参考代码:
void solve() {
int n;
cin >> n;
vector<int>last(n + 1, 0), a(n + 1, 0);
for (int i = 1; i <= n; ++i) {
cin >> a[i];
last[a[i]] = i;
}
int res = 0, pre = 0, cur = 0;
for (int i = 1; i <= n; ++i) {
pre = max(pre, last[a[i]]);
if (cur > i) ++res;
else cur = pre;
}
cout << res << '\n';
return;
}
作者:cherish.
出处:https://home.cnblogs.com/u/cherish-/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。