Educational Codeforces Round 184 ABCD1
A. Alice and Bob
- 取每个点只得 1 分,也就是让 Bob 能够取得的值最多。
- 我们发现我们总是可以让 Bob 取 $a + 1$ 或者 $a - 1$ . 我们只需要求出在这两种情况下,Bob 能够取得的数量是多少。这可以使用二分。
void solve() {
int n, x;
cin >> n >> x;
VI a(n);
For(i, 0, n) cin >> a[i];
int bscore = -1, ans = -1;
int tmp = n - (upper_bound(a.begin(), a.end(), x) - a.begin());
if (ckmax(bscore, tmp))
ans = x + 1;
tmp = n - (a.end() - lower_bound(a.begin(), a.end(), x));
if (ckmax(bscore, tmp))
ans = x - 1;
cout << ans << '\n';
}
B. Drifting Away
- 如果出现了以下几种情况,那么可以无限循环:
**,><,>*,*<
- 通过观察和模拟,我们发现如果没有无限循环,那么字符串中最多只有一个
*。如果出现了一次>,那么它的右侧只能出现>,不能再出现其他字符。如果出现了一次<,那么它的左侧只能出现<,不能出现其他字符。 - 总结一下,其实就是求字符串中,最长连续相同的子字符串的长度。
- 特殊判断一下字符串中只含有一个
*的情况,这种情况在样例中已经给出了。
void solve() {
string s;
cin >> s;
int n = SZ(s);
bool ok = false;
For(i, 0, n - 1) {
if ((s[i] == '>' && s[i + 1] == '<') || (s[i] == '>' && s[i + 1] == '*') ||
(s[i] == '*' && s[i + 1] == '<') || (s[i] == '*' && s[i + 1] == '*')) {
ok = true;
break;
}
}
if (ok) {
cout << "-1\n";
return;
}
int cnt = 0;
For(i, 0, n) {
if (s[i] == '*')
cnt++;
}
int cur = 1, ans = 1;
char ch = s[0];
For(i, 1, n) {
if (s[i] == s[i - 1]) {
cur++;
} else {
if (ckmax(ans, cur)) {
ch = s[i - 1];
}
cur = 1;
}
}
ckmax(ans, cur);
if (n > 1)
ans += cnt;
cout << ans << '\n';
}
C. Range Operation
- 很有意思的一道题目。
- 当区间 $[l, r]$ 变化的时候, $l + r$ 也会变化,很难找到单调性,也就是找不到贪心的性质。不妨从增量的角度考虑。
- 如果选择的区间是 $[l, r]$ ,数组和的增量是 $\Delta_{1} = (l + r) len - sum(l, r)$ ,其中 $len = r - l + 1$ .
- 如果把区间往右扩展一个位置变成 $[l, r + 1]$ , 数组和的增量是 $\Delta_2 = (l + r + 1)(len + 1) - sum(l, r + 1) = \Delta_1 + 2(r + 1) - a_{r + 1}$
- 我们发现一个很整齐的形式: $2(r + 1) - a_{r + 1}$
- 也就是说,把区间往右扩展一位,它的增量恰好就是 $2(r + 1) - a_{r + 1}$ 。也就是说每一个位置的贡献是 $2i - a_i$ 。
- 设数组 $b_i = 2i - a_i$ ,因为我们只能进行最多一次操作,也就是我们只需要取一段区间,使得这个区间的贡献最大,也就是取一段连续的 $b_i$ ,使得区间和最大。这是线性 DP 的例题:“求最大子数组和”。
void solve() {
int n;
cin >> n;
VL a(n + 1), b(n + 1);
ll sum{};
For1(i, 1, n) {
cin >> a[i];
sum += a[i];
b[i] = 2 * i - a[i];
}
VL d(n + 1);
ll ans = 0;
For1(i, 1, n) {
d[i] = max(b[i], d[i - 1] + b[i]);
ckmax(ans, d[i]);
}
ans = ans + sum;
cout << ans << '\n';
}
D1. Removal of a Sequence (Easy Version)
很有意思的一道题目。
如果要从删除掉了哪些数字的角度考虑,可能就有点复杂了。
这道题目的操作次数比较少,我们可以从全部操作完之后,剩下了多少个数字的角度考虑。对于给定的范围 $[1, n]$ ,操作完之后,剩下多少个数字是比较容易求出来的。
我们对 $n$ 进行二分,如果操作完之后,剩下的数字个数 $\ge k$ ,说明 $n$ 是合法的。我们找到最小的 $n$ ,此时 $n$ 就是答案。
void solve() {
ll x, y, k;
cin >> x >> y >> k;
auto check = [&](ll p) -> bool {
For(i, 0, x) { p -= p / y; }
return p >= k;
};
ll l = 1, r = 1e12, mid;
while (l < r) {
mid = (l + r) / 2;
if (check(mid))
r = mid;
else
l = mid + 1;
}
if (check(r))
cout << r << '\n';
else
cout << "-1\n";
}
