Official
B - 山頂への登山 / Climbing to the Summit Editorial by admin
GPT 5.2 High概要
各区間を順に進みながら体力を減らし、途中で一度でも体力が \(0\) 以下になったら以降は消費が倍になる、というルールをそのままシミュレーションして最終体力を求める問題です。
考察
重要な観察は次の2点です。
- 状態は「バテているかどうか」だけ
未来の行動を変える要素は「すでにバテたか」の1ビットしかありません。一度バテたら戻らないため、各区間でやることは「今の状態に応じて体力を引く →(必要なら)バテ状態にする → 小屋があれば足す」の繰り返しです。 - 山小屋の情報を高速に参照できれば、前から1回走査で終わる
各区間 \(i\) の直後に回復があるかどうかを毎回探す(例えば山小屋リストを線形探索する)と最悪で \(O(NM)\) になり、\(N, M \le 2 \times 10^5\) では間に合いません。
そこで、回復量をrec[i](区間 \(i\) の直後の回復量)として配列に入れておけば、各区間で \(O(1)\) で回復を足せます。
具体例(イメージ): - ある区間でバテていない状態で体力を引いた結果 \(S \le 0\) になったら、その瞬間から「バテた状態」になります。 - その直後に山小屋があって回復し、体力が正に戻っても バテ状態は解除されません(以降ずっと消費が倍)。
このルール順(消費→バテ判定→回復)を崩すとWAになるので注意が必要です。
アルゴリズム
- 入力で与えられる山小屋情報 \((P_j, R_j)\) を、配列
recに格納する。
rec[i] =区間 \(i\) 通過直後の回復量(なければ \(0\))
tired = False(バテていない)で開始し、区間 \(i = 1..N\) を順に処理する。- 体力消費:
tired == Trueなら \(S \leftarrow S - 2D_i\)
tired == Falseなら \(S \leftarrow S - D_i\)
- バテ判定:
tired == Falseかつ消費後に \(S \le 0\) ならtired = True
- 回復:
- \(i < N\) のときだけ(最後の区間の後に小屋はない)、\(S \leftarrow S + rec[i]\)
- 体力消費:
- ループ終了後の \(S\) を出力する(負でもそのまま)。
計算量
- 時間計算量: \(O(N + M)\)(回復配列の構築 \(M\)、シミュレーション \(N\))
- 空間計算量: \(O(N)\)(回復配列
recが主)
実装のポイント
処理順を厳守:消費 →(必要なら)バテ化 → 回復。回復してからバテ判定すると結果が変わります。
最後の区間の後は回復しない:問題文より区間 \(N\) 通過直後には山小屋が存在しないため、
i < Nのときだけ回復します。山小屋の入力順はバラバラなので、
rec[p] = rのように配列へ直接格納して対応します。ソースコード
import sys
def main():
data = list(map(int, sys.stdin.buffer.read().split()))
it = iter(data)
N = next(it)
M = next(it)
S = next(it)
D = [next(it) for _ in range(N)]
rec = [0] * (N + 1) # rec[i]: recovery right after segment i (1..N-1)
for _ in range(M):
p = next(it)
r = next(it)
rec[p] = r
tired = False
for i in range(1, N + 1):
di = D[i - 1]
if tired:
S -= 2 * di
else:
S -= di
if S <= 0:
tired = True
if i < N:
S += rec[i]
print(S)
if __name__ == "__main__":
main()
この解説は gpt-5.2-high によって生成されました。
posted:
last update: