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\)
询问至少需要经过多少次操作可以满足:

  1. \(\forall 1 \leq i \leq n\)\(S_{1, i} \neq 0\)
  2. \(\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)|\)

考虑如何修改:

  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\)
  2. \(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\) 的石堆,操作后有:

\[\begin{aligned} X &-= i \ s.t.\ X \geq 2i \\ Y &+= i \\ \end{aligned} \]

\(|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";
posted @ 2024-05-07 18:47  03Goose  阅读(32)  评论(0)    收藏  举报