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;
}
}
}
投稿日時:
最終更新: