Codeforces Round 1011 (Div. 2)
Codeforces Round 1011 (Div. 2)
闲话:我为什么没打这一场呢?因为我按照惯例准备报名截止前一刻(赛前五分钟)再报名,结果玩原神导致 22:31 才想起报名 /fn。
A. Serval and String Theory
长度为 \(n\) 的串是好的,当且仅当它的字典序严格小于它翻转过来的字典序。
一次操作可以交换 \(s_i, s_j \space(i \neq j)\)。最多交换 \(k\) 次,问能不能使得串变为好的。
\(n \le 100, 0 \le k \le 10^4\)。
完全不需要很多次操作。
如果串的每个值都相同显然无解。然后考虑可以 \(\le 2\) 次操作,分别把最小的放到 \(s_1\),最大的放到 \(s_n\),一定能保证串是好的了。
B. Serval and Final MEX
给定长度为 \(n\) 的序列。一次操作可以将一段长度大于 \(2\) 的区间替换为其 mex。
你需要让最终序列长度为 \(1\),且唯一的值为 \(0\)。
\(n \le 5000\),需要输出方案。
考虑可以先让所有连续的非 \(0\) 操作后变为 \(0\)。
然后就转化为给一车 \(0\),如何缩成只有一个 \(0\)。分成前缀后缀分别搞成 \(1\) 然后再操作变成 \(0\) 即可。
注意有 corner case:如果最终只剩下 \(\le 2\) 个 \(0\),怎么办呢?很简单,由于保证有解,第一步时把一段非 \(0\) 强制分开缩成 \(2\) 个 \(0\) 即可。
C. Serval and The Formula
给定 \(x, y\),求满足 \((x + k) + (y + k) = (x + k) \oplus (y + k)\) 的任意一个 \(k\)。
\(x, y \le 10^9, k \le 10^{18}\)。
\(x = y\) 显然不存在 \(k\)。
你从小到大逐位考虑,发现合法的话只能为 \(01, 10\) 或 \(00\)。如果遇到 \(11\),就把 \(k\) 的当前位进到下一位去,然后继续做这个过程。复杂度是 \(O(\log V)\) 的。
不过有简便做法:\(k = 2^t - \max(x, y)\) 一定是满足条件的(\(t\) 要足够大,使得 \(2^t > 10^9\))。
D. Serval and Kaitenzushi Buffet
\(n\) 次操作,第 \(i\) 次可以做如下选择:
\(r \leftarrow r + k, d \leftarrow d + a_i\);
\(r \leftarrow r - 1\)(进行该操作时必须 \(r > 0\));
不进行任何操作;
一开始 \(r = d = 0\),问最终使得 \(r = 0\) 的情况下 \(d\) 的最大值。
\(n \le 2 \times 10^5\)。
首先有显然的 \(O(nk)\) DP:设 \(f_{i, j}\) 表示前 \(i\) 个数,当前 \(r = j\) 的最大 \(d\)(如果 \(r > n - i\) 则最终一定无法消为 \(0\))。
然后你有一个观察:如果我们选定了哪几个操作是操作 \(1\),那么对操作 \(2\) 我们会尽量往后放,使得不会出现操作时 \(r = 0\) 的情况。
于是倒数第 \(i\) 次操作 \(1\) 一定是在第 \(\le n - i(k + 1) + 1\) 次操作(称之为关键操作)进行的。
解释一下含义:由于倒数第 \(i\) 次操作后 \(r \ge k\),所以后面至少要安排 \(k\) 次操作 \(2\) 以抵消贡献,一共花费至少 \(k + 1\) 次操作。
于是用堆维护当前的最大值,在关键操作的时候判断一下即可。
具体的,设当前是第 \(i\) 次操作,是倒数第 \(j\) 次操作 \(1\)。于是满足 \(i \le n - j(k + 1) + 1\),即 \(j(k + 1) \le n - i + 1\)。
于是当 \(j(k + 1) = n - i + 1\) 时将堆顶计入答案并弹出即可。显然最优。
Code
#include <bits/stdc++.h>
#define _for(i, a, b) for (int i = (a); i <= (b); i ++ )
#define ll long long
using namespace std;
const int N = 4e5 + 5;
int n, k, a[N]; ll ans; priority_queue<int> q;
inline void chkmin(int & x, int y) { x = x < y ? x : y; }
inline void chkmax(int & x, int y) { x = x > y ? x : y; }
inline void solve() {
cin >> n >> k, ans = 0;
while (q.size()) q.pop();
_for (i, 1, n) {
cin >> a[i], q.push(a[i]);
if ( ! ((n - i + 1) % (k + 1))) ans += q.top(), q.pop();
} cout << ans << "\n";
} signed main() {
ios :: sync_with_stdio(false), cin.tie(0), cout.tie(0);
int T; cin >> T; while (T -- ) solve();
return 0;
}
E. Serval and Modulo
给定 \(a_i, b_i\),满足 \(b_i = a_i \bmod k\)。
\(b\) 是打乱后给出,你需要求出一个可行的 \(k\) 或报告无解。
\(n \le 10^4, a_i, b_i \le 10^6\)。
设 \(x = \sum_{i = 1}^n a_i - \sum_{i = 1}^n b_i\),显然 \(k \mid x\)。
\(x \le 10^{10}\),我们可以直接枚举 \(x\) 的约数(最多约 \(t = 2300\) 个)进行判断即可,单次判断是 \(O(n)\) 的。
复杂度 \(O(tn)\)。
Code
#include <bits/stdc++.h>
#define _for(i, a, b) for (int i = (a); i <= (b); i ++ )
#define ll long long
using namespace std;
const int N = 1e6 + 5, V = 1e9;
int n, flag, a[N], b[N], cnt[N], cnt1[N], cnt2[N]; ll s;
inline bool valid(int x) {
_for (i, 1, n) cnt2[a[i] % x] ++ ; int ok = 1;
_for (i, 1, n) if (cnt2[a[i] % x] ^ cnt1[a[i] % x]) { ok = 0; break; }
_for (i, 1, n) cnt2[a[i] % x] -- ; return ok;
} inline void solve() {
_for (i, 1, n) cnt1[b[i]] -- ; cin >> n, s = 0, flag = 1;
_for (i, 1, n) cin >> a[i], s += a[i]; sort(a + 1, a + n + 1);
_for (i, 1, n) cin >> b[i], s -= b[i], cnt1[b[i]] ++ ; sort(b + 1, b + n + 1);
_for (i, 1, n) if (a[i] ^ b[i]) { flag = 0; break; }
if (flag) return cout << V << "\n", void();
_for (i, 1, sqrt(s)) if ( ! (s % i)) {
if (valid(i)) return cout << i << "\n", void();
if (s / i <= V && valid(s / i)) return cout << s / i << "\n", void();
} cout << - 1 << "\n";
} signed main() {
ios :: sync_with_stdio(false), cin.tie(0), cout.tie(0);
int T; cin >> T; while (T -- ) solve();
return 0;
}
F1. Serval and Colorful Array (Easy Version)
长度为 \(n\) 值域为 \(k\) 的序列。一次操作可以交换相邻两个值,问最少交换多少次使得序列的一个子序列成为一个 \([1, k]\) 的排列。
\(n \le 5000\)。
考虑枚举中间点 \(i\),然后问题转化为将 \([1, k]\) 划分成两半,然后算距离贡献。
先把距离贡献转化为到 \(i\) 的距离,而不是到最终该值的位置的距离。考虑求出 \(i\) 左边和右边的每个值 \(j\) 到 \(i\) 的最近距离 \(l_j, r_j\)。先钦定每个数都塞在左边,然后按照 \(r_j - l_j\) 从小到大依次扔到右边,扔了一半以后就是以 \(i\) 为中心的最优答案了。
复杂度 \(O(n^2 \log n)\)。
Code
#include <bits/stdc++.h>
#define _for(i, a, b) for (int i = (a); i <= (b); i ++ )
#define ll long long
using namespace std;
const int N = 4e5 + 5, INF = 1e9 + 5;
int n, k, a[N], l[N], r[N], val[N]; ll s, ans;
inline void chkmin(int & x, int y) { x = x < y ? x : y; }
inline void solve() {
cin >> n >> k, ans = (ll)1e18 + 5;
_for (i, 1, n) cin >> a[i];
_for (i, 1, n) {
s = 0;
_for (j, 1, k) l[j] = r[j] = INF;
_for (j, 1, i) chkmin(l[a[j]], i - j);
_for (j, i, n) chkmin(r[a[j]], j - i);
_for (j, 1, k) s += l[j], val[j] = r[j] - l[j]; sort(val + 1, val + k + 1);
_for (j, 1, (k + 1) >> 1) s += val[j]; ans = min(ans, s);
} _for (i, 1, k) ans -= abs(i - (k + 1) / 2); cout << ans << "\n";
} signed main() {
ios :: sync_with_stdio(false), cin.tie(0), cout.tie(0);
int T; cin >> T; while (T -- ) solve();
return 0;
}
F2. Serval and Colorful Array (Hard Version)
\(n \le 4 \times 10^5\)。
似乎大力数据结构优化即可,可以做到 \(O(n \log n)\)。

浙公网安备 33010602011771号