C - 山の見える展望台 / Observatory with a Mountain View Editorial by admin Editorial for printing
Gemini 3.0 Flash (Thinking)概要
この問題は、与えられた \(N\) 個のデータ(山の標高と美しさ)に対して、「標高が一定値以上のものの合計値」を求めるクエリに \(Q\) 回答える問題です。
考察
素朴な方法とその限界
各クエリに対して、すべての山を一つずつチェックして標高が \(L_j\) 以上か判定し、合計を計算する方法(線形探索)が考えられます。 しかし、この方法では 1 回のクエリに \(O(N)\) の時間がかかり、全体で \(O(N \times Q)\) の計算量となります。 本問題の制約では \(N, Q \le 2 \times 10^5\) であるため、最大で \(4 \times 10^{10}\) 回程度の計算が必要になり、実行時間制限に間に合いません。
高速化のポイント
「特定の範囲の合計を素早く求める」ために、以下の 2 つの工夫を組み合わせます。
- ソートと二分探索: 山を標高の低い順に並べ替えておけば、「標高が \(L_j\) 以上の山がどこから始まるか」を二分探索によって \(O(\log N)\) で見つけることができます。
- 累積和(後ろから): ある地点から最後までの合計値をあらかじめ計算しておく累積和(逆方向)を用いることで、範囲内の合計値を \(O(1)\) で取得できます。
これにより、各クエリに対して \(O(\log N)\) で回答できるようになります。
アルゴリズム
- データの整理: 山の標高 \(Y_i\) と美しさ \(P_i\) をペアとしてまとめ、標高 \(Y\) の昇順にソートします。
- 累積和の計算: ソートした後の美しさの配列に対して、後ろから順に合計を計算した配列
suffix_sumsを作成します。suffix_sums[i]= (ソート後の \(i\) 番目から最後までの美しさの合計)suffix_sums[N] = 0としておくと便利です。
- クエリへの回答: 各基準標高 \(L_j\) に対して以下を行います。
- 二分探索(Pythonでは
bisect_left)を用いて、ソート済みの標高リストの中で \(L_j\) 以上となる最小のインデックスidxを見つけます。 suffix_sums[idx]が求める答えとなります。
- 二分探索(Pythonでは
計算量
- 時間計算量: \(O((N + Q) \log N)\)
- 山のソートに \(O(N \log N)\)
- 累積和の構築に \(O(N)\)
- \(Q\) 回の二分探索に \(O(Q \log N)\) かかります。
- 空間計算量: \(O(N + Q)\)
- 山のデータ、累積和配列、およびクエリの回答を保持するために必要です。
実装のポイント
高速な入出力: \(N\) と \(Q\) が大きいため、Pythonでは
sys.stdin.read().split()やsys.stdout.writeを使用して入出力のボトルネックを解消しています。二分探索: 標高がちょうど \(L_j\) の山が含まれる場合も考慮し、
bisect_leftを使用して「\(L_j\) 以上」の境界を正しく取得します。累積和のインデックス:
suffix_sumsのサイズを \(N+1\) にすることで、すべての山が条件を満たさない(idx = Nとなる)場合でもエラーにならず0を返せるようにしています。ソースコード
import sys
from bisect import bisect_left
def solve():
# 入力を一度にすべて読み込む(高速化のため)
data = sys.stdin.read().split()
if not data:
return
# すべての文字列を整数に変換
nums = list(map(int, data))
N = nums[0]
Q = nums[1]
# 山の標高(Y)と評価値(P)をスライスで抽出
# Yはインデックス 2, 4, ..., 2 + 2*N - 2
# Pはインデックス 3, 5, ..., 2 + 2*N - 1
Y = nums[2:2+2*N:2]
P = nums[3:2+2*N:2]
# 標高 Y に基づいて山をソートする
# zipを使用して (標高, 評価値) のペアを作成しソート
mountains = sorted(zip(Y, P))
# ソートされた結果から標高と評価値をそれぞれ分離
if N > 0:
heights, beauties = zip(*mountains)
else:
heights, beauties = (), ()
# 評価値の累積和(後ろから)を計算
# suffix_sums[i] は、ソート後の i 番目以降のすべての山の評価値の合計
suffix_sums = [0] * (N + 1)
for i in range(N - 1, -1, -1):
suffix_sums[i] = suffix_sums[i+1] + beauties[i]
# 調査(クエリ)の基準標高 L は、山のデータ以降に格納されている
queries = nums[2+2*N:]
results = []
# 各調査に対して二分探索を用いて答えを求める
for l in queries:
# bisect_left で標高が l 以上となる最初のインデックスを取得
idx = bisect_left(heights, l)
# そのインデックス以降の評価値の合計を累積和配列から取得
results.append(str(suffix_sums[idx]))
# すべての答えを改行区切りで一括出力
sys.stdout.write('\n'.join(results) + '\n')
if __name__ == '__main__':
solve()
この解説は gemini-3-flash-thinking によって生成されました。
posted:
last update: