AtCoder Beginner Contest 059
A
给三个空格隔开的由小写字母组成的字符串,连续输出它们的首字母的大写。
std::string s; std::getline(std::cin, s);
for (int i = 0; i < (int)s.size(); i++) {
if (i - 1 == -1 || s[i - 1] == ' ') std::cout << char(s[i] - 'a' + 'A');
}
std::cout << "\n";
B
题意:
顺序给两个正整数 \(A, B\) ,比较大小。输出 \(GREATER, LESS, EQUAL\) 之一。
\(1 \leq A, B \leq 10^{100}\)
题解:
注意到 \(A, B\) 很大。
如果位数不一样,则大小显然。
如果位数一样,则大小是字典序。
std::string A, B; std::cin >> A >> B;
if (A.size() != B.size()) std::cout << (A.size() < B.size() ? "LESS" : "GREATER") << "\n";
else std::cout << (A < B ? "LESS" : A > B ? "GREATER" : "EQUAL") << "\n";
时间复杂度 \(O(|A|)\) 。
C
题意:
给一个长度为 \(n\) 的序列 \(a_1, a_2, a_3, \cdots, a_n\) 。可以执行任意次操作:选择任意一个 \(i(1 \leq i \leq n)\) ,让 \(a_i\) 加上或减少 \(1\) 。
定义 \(S_{l, r} = \sum_{i = l}^{r} a_i\) 。
询问至少需要经过多少次操作可以满足:
- \(\forall 1 \leq i \leq n\) ,\(S_{1, i} \neq 0\) 。
- \(\forall 1 \leq i \leq n - 1\) ,\(S_{1, i} \times S_{1, i + 1} < 0\) 。
题解:
先证贪心是对的。
如果 \(a_1\) 确定,那么 \(a_i\) 一定是尽可能不变,直到 \(S_{1, i}\) 不合法。
否则 \(i\) 改变,不会让后续的 \(j > i\) 能够消耗更少的代价,最多只是存在一种方案使得结果相同。
于是就是一个经典问题。只需要确定第一位,所有位置都能被确定。
第一位要么不变,要么负数变为 \(1\) ,要么正数变为 \(-1\) 。
分正负讨论即 \(a_1\) 有且会有 \(max(1, a_1)\) 和 \(min(-1, a_1)\) 两种可能。分别贡献 \(|a_1 - max(1, a_1)|, |a_1 - min(-1, a_1)|\) 。
考虑如何修改:
- 若 \(S_{1, i - 1} > 0\) 且 \(S_{1, i - 1} + a_i \geq 0\) ,则 \(inc = S_{1, i - 1} + a_i + 1\) 。\(res += inc, a_i -= inc\) 。
- 若 \(S_{1, i - 1} < 0\) 且 \(S_{1, i - 1} + a_i \leq 0\) ,则 \(inc = 1 - (S_{1, i - 1} + a_i)\) 。\(res += inc, a_i += inc\) 。
int n; std::cin >> n;
std::vector<int> a(n + 1);
for (int i = 1; i <= n; i++) {
std::cin >> a[i];
}
ll ans = llinf;
auto sol = [&] (std::vector<int> vec) -> ll {
ll res = 0, pre = vec[0];
for (int i = 1; i < vec.size(); i++) {
int inc = 0;
if (pre > 0 && pre + vec[i] >= 0) {
inc = pre + vec[i] + 1;
vec[i] -= inc;
}
else if (pre < 0 && pre + vec[i] <= 0) {
inc = 1 - (pre + vec[i]);
vec[i] += inc;
}
res += inc;
pre += vec[i];
}
return res;
};
std::vector<int> b(a.begin() + 1, a.end()), c(a.begin() + 1, a.end());
int cur1 = std::max(1, a[1]), cur2 = std::min(-1, a[1]);
b[0] = cur1; c[0] = cur2;
ans = std::min<ll>(sol(b) + 1LL * abs(a[1] - cur1), sol(c) + 1LL * abs(a[1] - cur2));
std::cout << ans << "\n";
D
题意:
\(Alice\) 和 \(Brown\) 在玩一个游戏。在这个游戏中,有两个堆分别有 \(X\) 和 \(Y\) 堆石子,\(Alice\) 和 \(Brown\) 轮流操作。
\(Alice\) 先开始:
- 在其中一堆选择 \(2i\) 个石子,丢掉 \(i\) 个,然后让剩下的 \(i\) 个放入另一堆石子。\(i \geq 1\) 可以任意选择,只要选择的石堆石子剩余超过 \(2i\) 。
\(Alice\) 和 \(Brown\) 都会执行最优策略。询问最终谁会获胜。
\(0 \leq X, Y \leq 10^{18}\)
题解:
我似乎曾不太明白他的 key 是什么,但我写出过类似的题目。
题解告诉我 key 在于 \(|X - Y|\) 。
首先具象化操作,不妨选择的是数量为 \(X\) 的石堆,操作后有:
若 \(|X - Y| \leq 1\) ,则 \(Bob\) 会赢。否则 \(Alice\) 会赢。
现在开始证明。
若 \(|X - Y| \leq 1\) ,初始一定有 \(X \geq 2i, Y \geq 2i\) 。
于是 \(X -= i, Y += i\) 后,操作一定可逆。
无论 \(Alice\) 如何操作,她一定会使得 \(|X - Y| > 1\) 。
\(Bob\) 可以逆操作使 \(|X - Y| \geq i\)。
若 \(|X - Y| > 1\) ,不妨让 \(X > Y\) ,则有 \(X \geq 2 \frac{X - Y}{2}\) 。
于是只需让 \(X -= \lfloor \frac{X - Y}{2} \rfloor, Y += \lfloor \frac{X - Y}{2} \rfloor\) 。于是 \(X - Y = \{0, 1\}\) 。
于是,答案永远可以被归约到 \(|X - Y| \leq 1\) 。
考虑规约态的最终规约 \(0, 1\) 。此时没有偶数石子的石堆可供玩家选择,是个必败态。
ll X, Y; std::cin >> X >> Y;
std::cout << (llabs(X - Y) <= 1 ? "Brown" : "Alice") << "\n";
浙公网安备 33010602011771号