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\) 次可以做如下选择:

  1. \(r \leftarrow r + k, d \leftarrow d + a_i\)

  2. \(r \leftarrow r - 1\)(进行该操作时必须 \(r > 0\));

  3. 不进行任何操作;

一开始 \(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)\)

posted @ 2025-03-22 22:56  Ray_Wu  阅读(460)  评论(0)    收藏  举报