C - 街灯と吹雪 / Streetlights and Blizzard Editorial by admin
Qwen3-Coder-480B概要
\(N\) 本の街灯があり、それぞれに初期耐久値がある。\(M\) 回の吹雪により、各区間に属する街灯の耐久値が減少する。すべての吹雪が終わった後に、耐久値が 1 以上の街灯の本数を求めよ。
考察
この問題では、単純に各吹雪ごとに区間 \([L_j, R_j]\) の街灯の耐久値を直接減らす処理を行うと、最悪の場合で \(O(N \cdot M)\) の計算量となり、制約 \(N, M \leq 2 \times 10^5\) では間に合いません(TLE します)。
重要な観察は、各街灯に対する吹雪によるダメージの総和だけが必要であり、途中経過は不要だということです。そのため、区間加算を効率的に処理する方法があれば高速化できます。
そこで 差分配列 を使うことを考えます。これは、区間に対して一定の値を加える操作を、配列の始点と終点+1にだけ値を記録することで、\(O(1)\) で処理できる手法です。その後、累積和を取ることで各要素への加算結果を得られます。
例えば、街灯の数 \(N=5\) として、吹雪が区間 \([2, 4]\) に \(D=3\) のダメージを与える場合を考えると:
- 差分配列
diffに対して:diff[1] += 3(0-indexedで2番目)diff[4] -= 3(0-indexedで5番目、ただし存在しないので外側に持っていく)
- 累積和を取ると、区間 \([1, 3]\)(0-indexed)に \(3\) が反映される
このようにして、各吹雪の区間減算を高速に処理し、最後に一度だけ累積和を取って各街灯の総ダメージを計算します。
アルゴリズム
- 初期の耐久値配列 \(H\) を読み込む。
- 長さ \(N+1\) の差分配列
diffを用意する(0で初期化)。 - 各吹雪について:
- 区間 \([L_j, R_j]\) に \(D_j\) のダメージを加えるために:
diff[L_j - 1] += D_jdiff[R_j] -= D_j
- 区間 \([L_j, R_j]\) に \(D_j\) のダメージを加えるために:
- 差分配列の累積和を取り、各街灯へのダメージ合計
damagesを得る。 - 各街灯について、最終耐久値 \(H[i] - damages[i]\) を求め、それが 1 以上ならカウントアップ。
- カウントした数を出力。
計算量
- 時間計算量: \(O(N + M)\)
各吹雪の処理が定数時間で終わり、最後に累積和と判定処理が \(O(N)\)。 - 空間計算量: \(O(N)\)
差分配列とダメージ配列に \(O(N)\) 必要。
実装のポイント
吹雪の区間は 1-indexed で与えられるので、配列アクセス時は 0-indexed に変換すること(つまり \(L-1\), \(R\) を使う)。
差分配列のサイズは \(N+1\) にしておくことで、右端の更新
diff[R] -= Dを安全に行える。最終的に累積和を取る際、
diff[:-1]の部分までを使う(最後の余分な要素は除外)。ソースコード
import sys
from itertools import accumulate
def main():
import sys
input = sys.stdin.read
data = input().split()
idx = 0
N = int(data[idx])
idx += 1
M = int(data[idx])
idx += 1
H = list(map(int, data[idx:idx+N]))
idx += N
# 差分配列を用意
diff = [0] * (N + 1)
for _ in range(M):
L = int(data[idx])
idx += 1
R = int(data[idx])
idx += 1
D = int(data[idx])
idx += 1
diff[L-1] += D
diff[R] -= D
# 累積和で各区間への減少量を求める
damages = list(accumulate(diff[:-1]))
# 各街灯の最終耐久値を計算し、1以上か判定
count = 0
for i in range(N):
final_health = H[i] - damages[i]
if final_health >= 1:
count += 1
print(count)
if __name__ == "__main__":
main()
この解説は qwen3-coder-480b によって生成されました。