B - お菓子の公平な分配 / Fair Distribution of Sweets Editorial by admin Editorial for printing
Claude 4.6 Opus (Thinking)概要
\(N\) 人の子どもが持つお菓子を「最大と最小の差が \(1\) 以下」になるまで均す操作の回数を求める問題です。最終状態を直接計算し、そこへ到達するのに必要な操作回数を求めます。
考察
最終状態はどうなるか?
お菓子の総数 \(S = \sum A_i\) は操作を通じて変わりません。最終状態ではどの2人の差も \(1\) 以下なので、\(q = \lfloor S/N \rfloor\)、\(r = S \bmod N\) とすると:
- \(N - r\) 人が \(q\) 個を持つ
- \(r\) 人が \(q + 1\) 個を持つ
という状態に必ずなります。
操作回数はどう数えるか?
1 回の操作で「最大の子から 1 個減らし、最小の子に 1 個増やす」ので、操作回数は 目標より多く持っている子から回収するお菓子の総数 に等しくなります(回収した分がそのまま不足している子に渡される)。
素朴なシミュレーションがダメな理由
\(A_i\) が最大 \(10^9\) なので、1 個ずつ移す操作を愚直にシミュレーションすると、操作回数が膨大(最悪 \(O(N \times 10^9)\) 程度)になり TLE します。
最適な割り当てを考える
ソートした \(A\) に対して、ソートした目標値を割り当てれば、各子どもが「目標からどれだけずれているか」を直接計算できます。具体的には、\(A\) を昇順にソートし、小さい方から \(N - r\) 人に目標 \(q\) を、残り \(r\) 人に目標 \(q + 1\) を割り当てます。
具体例: \(N = 4\), \(A = [1, 8, 3, 4]\) → \(S = 16\), \(q = 4\), \(r = 0\)
- ソート後: \([1, 3, 4, 8]\)
- 目標: \([4, 4, 4, 4]\)
- 差分: \([-3, -1, 0, +4]\)
- 超過分の合計: \(4\) → 答えは \(4\)
アルゴリズム
- \(S = \sum A_i\) を求め、\(q = \lfloor S/N \rfloor\), \(r = S \bmod N\) を計算する。
- \(A\) を昇順にソートする。
- ソートした \(A\) の先頭 \(N - r\) 個に目標 \(q\) を、残り \(r\) 個に目標 \(q + 1\) を割り当てる。
- \(A_i\) が目標を超えている部分の合計 \(\sum \max(A_i - \text{target}_i, 0)\) を求める。これが答え。
計算量
- 時間計算量: \(O(N \log N)\)(ソートがボトルネック)
- 空間計算量: \(O(N)\)(配列の保持)
実装のポイント
\(A_i\) が最大 \(10^9\)、\(N\) が最大 \(5 \times 10^5\) なので、答えは非常に大きくなりうる。64 bit 整数(Python では自動対応)を使う。
目標を超えている分の合計 だけ を足せばよい(不足分の合計と一致するため、どちらか片方で OK)。
sys.stdin.buffer.read()を使うことで大量入力を高速に処理している。ソースコード
import sys
def solve():
data = sys.stdin.buffer.read().split()
N = int(data[0])
A = [int(data[i + 1]) for i in range(N)]
S = sum(A)
q, r = divmod(S, N)
# Sort A in non-decreasing order
A.sort()
# Sorted targets: first (N - r) children get q, last r children get q + 1
ans = 0
for i in range(N):
if i < N - r:
target = q
else:
target = q + 1
if A[i] > target:
ans += A[i] - target
print(ans)
solve()
この解説は claude4.6opus-thinking によって生成されました。
posted:
last update: