F - Divide the Cake 解説 by shinchan


結論は公式解説と似ていますが、別の考察方針をしてみます。

以下の考察はすべて0-indexedでされていることに注意してください(切れ込みも \(0\)\(N-1\) とします)。

\(S\)\(S_0 = 0, S_{i+1} = S_i + A_i ~(0 \leq i < N)\) で定義される数列とします。つまり累積和です。また、問題文の通り、高橋君が選ぶ切れ込みを \(i\) 、青木君が選ぶ切れ込みを \(j\) とします。 \(i < j\)\(i > j\) に場合分けして考えます。

\(i < j\) のとき

高橋君がもらったピースのイチゴの個数の平均値は以下で表せます。これはイチゴの総数をピースの個数で割ったものになります。

\[\frac{S_j - S_i}{j - i}\]

一方、青木君がもらったピースのイチゴの個数の平均値は以下で表せます。青木君のピースは切れ込み \(0\) をまたぐため一巡してこのような式となります。

\[\frac{(S_{N} + S_{i}) - S_{j}}{(N + i) - j} \]

つまり、青木君は高橋君が選んだ \(i\) に応じて、以下を満たすような \(j\) を探すことになります。

\[\frac{S_j - S_i}{j - i} < \frac{(S_{N} + S_{i}) - S_{j}}{(N + i) - j} \]

この式は変形すると、以下のようになります。

\[\frac{S_j - S_i}{j - i} < \frac{(S_{N} + S_{i}) - S_{j}}{(N + i) - j} \]

\[(S_j - S_i) (N + i - j) < (S_N + S_i - S_j)(j - i) \]

\[ (S_j - S_i) N < S_N (j - i) \]

\[ N S_j - j S_N < N S_i - i S_N \]

よって、高橋君も青木君も、\(N S_i - i S_N\) がより小さくなる \(i\) を選びたいことがわかります。 \(i > j\) の方も進めてみましょう。

\(i > j\) のとき

同様の考察で、青木君は高橋君が選んだ \(i\) に応じて、以下を満たすような \(j\) を探すことになります。

\[ \frac{(S_{N} + S_{j}) - S_{i}}{(N + j) - i} < \frac{S_i - S_j}{i - j} \]

この式を変形すると、 以下のように \(i < j\) のときと同じ式が現れます。

\[ N S_j - j S_N < N S_i - i S_N \]

まとめ

以上より、高橋君は、\(N S_i - i S_N\) が最小となる \(i\) を選べば、必ず勝てることがわかりました。\(N S_i - i S_N\) が最小となる \(i\) のうち最小のものが答えとなります。計算量は \(O(N)\) です。

実装例 (C++)

https://atcoder.jp/contests/codequeen2024-final-N9tn8QqD/submissions/56660864

#include <bits/stdc++.h>
using namespace std;
using ll = long long;

int main() {
    ll n; cin >> n;
    vector<ll> s(n + 1, 0LL);
    for(ll i = 1; i <= n; i ++) {
        ll a; cin >> a;
        s[i] = s[i - 1] + a;
    }
    ll Min = 1LL << 60;
    for(ll i = 0; i < n; i ++) Min = min(Min, n * s[i] - i * s[n]);
    for(ll i = 0; i < n; i ++) {
        if(Min == n * s[i] - i * s[n]) {
            cout << i + 1 << endl;
            break;
        }
    }
}

投稿日時:
最終更新: