D - 気温の統一 / Uniform Temperature Editorial by admin
Gemini 3.0 Flash (Thinking)概要
\(N\) 個の部屋の室温をある整数 \(T\) に統一する際、各部屋の広さ \(S_i\) を重みとした絶対誤差の総和 \(\sum S_i |P_i - T|\) を最小化する問題です。これは統計学における「加重中央値」を求める問題に帰着されます。
考察
1. 目的関数の性質
最小化したい関数 \(f(T) = \sum_{i=1}^{N} S_i |P_i - T|\) について考えます。 もしすべての \(S_i\) が \(1\) であるならば、この関数を最小にする \(T\) は \(P_i\) の中央値になることが知られています。 本問では各 \(P_i\) に重み \(S_i\) がついているため、加重中央値(重み付き中央値)を求めることになります。
2. なぜ中央値なのか
関数 \(f(T)\) は凸関数(下に凸なグラフ)であり、その傾きは \(T\) を増加させたときに以下のように変化します。 - \(T\) を \(1\) 増やしたとき、現在の \(T\) より小さい \(P_i\) を持つ部屋のコストは \(S_i\) ずつ増え、大きい \(P_i\) を持つ部屋のコストは \(S_i\) ずつ減ります。 - つまり、傾きが負から正に転じるポイント(累積の重みが全体の半分を超える場所)が最小値となります。
3. \(T\) が整数であるという制約
問題文には「\(T\) は整数」という制約がありますが、関数の折れ曲がる点(傾きが変わる点)はすべて入力の \(P_i\)(整数)の位置にあります。したがって、加重中央値となる \(P_k\) は必ず整数であり、そのまま最適な \(T\) として採用できます。
アルゴリズム
- 各部屋の情報を温度 \(P_i\) と広さ \(S_i\) のペアとして取得し、温度 \(P_i\) の昇順にソートします。
- 全部屋の広さの合計 \(W = \sum S_i\) を計算します。
- ソートした順に広さ \(S_i\) を累積していき、累積和が \(W/2\) 以上になった瞬間の温度 \(P_k\) を目標温度 \(T\) とします。
- 求めた \(T\) を用いて、各部屋のコストの総和 \(\sum S_i |P_i - T|\) を計算して出力します。
計算量
- 時間計算量: \(O(N \log N)\)
- 部屋のソートに \(O(N \log N)\) かかります。その後の累積和の計算とコストの計算は \(O(N)\) です。
- 空間計算量: \(O(N)\)
- \(N\) 個の部屋の情報をリストに保持するために必要です。
実装のポイント
高速な入出力: \(N\) が最大 \(150,000\) と大きいため、Python の場合は
sys.stdin.read().split()などを用いて一括で入力を読み込むと実行時間を短縮できます。加重中央値の判定: 累積和を
current_wとすると、current_w * 2 >= total_wという条件(分母を払った形)で判定することで、浮動小数点の誤差を避けつつ「半分以上」を正確に判定できます。大きな値の扱い: 最終的なコストは非常に大きな値になる可能性がありますが、Python は標準で多倍長整数をサポートしているため、オーバーフローを気にせず計算できます。
ソースコード
import sys
def solve():
# Fast I/O: read all input at once and split by whitespace
try:
input_data = sys.stdin.read().split()
except EOFError:
return
if not input_data:
return
it = iter(input_data)
try:
line = next(it)
except StopIteration:
return
N = int(line)
# The problem asks to minimize the function f(T) = sum(S_i * |P_i - T|)
# where T is an integer. This is a weighted version of the median problem.
# Since f(T) is a convex, piecewise linear function with corners at integer
# coordinates P_i, the minimum value will be achieved at one of the P_i.
rooms = []
# Each room has a size S_i and a temperature P_i
for _ in range(N):
try:
s = int(next(it))
p = int(next(it))
rooms.append((p, s))
except StopIteration:
break
# Sort the rooms by temperature P_i to find the weighted median
# This operation is O(N log N)
rooms.sort()
# Calculate the total weight (sum of all sizes S_i)
total_weight = sum(r[1] for r in rooms)
# The weighted median target_t is the first temperature P_i where the
# cumulative weight reaches or exceeds half of the total weight.
cumulative_weight = 0
target_t = rooms[0][0]
for p, s in rooms:
cumulative_weight += s
if cumulative_weight * 2 >= total_weight:
target_t = p
break
# Calculate the minimum total cost using the found target_t
# Total cost = sum(S_i * |P_i - target_t|)
min_total_cost = 0
for p, s in rooms:
min_total_cost += s * abs(p - target_t)
# Output the result as an integer
sys.stdout.write(str(min_total_cost) + '\n')
if __name__ == '__main__':
solve()
この解説は gemini-3-flash-thinking によって生成されました。
posted:
last update: