C - 街灯と吹雪 / Streetlights and Blizzard Editorial by admin
Claude 4.6 Opus (Thinking)概要
\(N\) 本の街灯に対して \(M\) 回の吹雪(区間に対する一括減算)が行われた後、耐久値が \(1\) 以上残っている街灯の本数を求める問題です。いもす法を用いることで、区間への加算を効率的に処理できます。
考察
素朴なアプローチとその問題点
最も単純な方法は、各吹雪ごとに街灯 \(L_j\) から \(R_j\) まで1本ずつ耐久値を \(D_j\) 減らすことです。しかし、この方法では1回の吹雪で最大 \(N\) 本の街灯を更新する必要があり、全体で最悪 \(O(N \times M)\) の計算量になります。\(N, M\) がともに最大 \(2 \times 10^5\) なので、\(4 \times 10^{10}\) 回の操作となり、制限時間に間に合いません(TLE)。
重要な気づき
各街灯 \(i\) について最終的に知りたいのは、すべての吹雪によるダメージの合計値 \(\text{damage}[i]\) です。つまり:
\[\text{damage}[i] = \sum_{\substack{j : L_j \leq i \leq R_j}} D_j\]
これが分かれば、\(H_i - \text{damage}[i] \geq 1\) かどうかを判定するだけです。
「区間に一律の値を加算し、最後に各要素の合計を知りたい」という操作は、いもす法(差分配列) で \(O(1)\) ずつ処理できます。
アルゴリズム
いもす法の仕組み
長さ \(N+1\) の差分配列 \(\text{diff}\) を用意します(初期値はすべて \(0\))。
各吹雪 \((L_j, R_j, D_j)\) に対して: - \(\text{diff}[L_j - 1]\) に \(D_j\) を加算 - \(\text{diff}[R_j]\) から \(D_j\) を減算
具体例: \(N = 5\) で吹雪 \((L=2, R=4, D=3)\) の場合
| index | 0 | 1 | 2 | 3 | 4 |
|---|---|---|---|---|---|
| diff | 0 | +3 | 0 | 0 | -3 |
この差分配列の累積和を取ると:
| index | 0 | 1 | 2 | 3 | 4 |
|---|---|---|---|---|---|
| damage | 0 | 3 | 3 | 3 | 0 |
街灯 2〜4(0-indexed で 1〜3)にダメージ \(3\) が正しく反映されています。
全体の手順
- 差分配列 \(\text{diff}\) を用意する
- \(M\) 回の吹雪それぞれについて、差分配列に \(O(1)\) で記録する
- 差分配列の累積和を取りながら、各街灯 \(i\) のダメージ合計 \(\text{cumulative}\) を計算する
- \(H_i - \text{cumulative} \geq 1\) なら倒壊していないのでカウントする
- 最終的なカウントを出力する
計算量
- 時間計算量: \(O(N + M)\)
- 差分配列への記録が \(O(M)\)、累積和の計算と判定が \(O(N)\)
- 空間計算量: \(O(N)\)
- 差分配列と耐久値配列にそれぞれ \(O(N)\)
実装のポイント
1-indexed と 0-indexed の変換: 問題文では街灯は \(1\) から番号が振られていますが、配列は \(0\)-indexed なので、\(L_j - 1\) の位置に加算し、\(R_j\) の位置で減算します。
オーバーフローに注意: \(D_j\) が最大 \(10^9\) で \(M\) が最大 \(2 \times 10^5\) なので、ダメージ合計は最大 \(2 \times 10^{14}\) 程度になり得ます。Python では整数のオーバーフローがないため問題ありませんが、C++ などでは
long longを使う必要があります。高速な入力: \(N, M\) が大きいため、Python では
sys.stdin.buffer.read()でまとめて読み込むことで入力を高速化しています。ソースコード
import sys
def main():
input_data = sys.stdin.buffer.read().split()
idx = 0
N = int(input_data[idx]); idx += 1
M = int(input_data[idx]); idx += 1
H = [int(input_data[idx + i]) for i in range(N)]
idx += N
# imos法で各街灯へのダメージ合計を計算
diff = [0] * (N + 1)
for j in range(M):
L = int(input_data[idx]); idx += 1
R = int(input_data[idx]); idx += 1
D = int(input_data[idx]); idx += 1
diff[L - 1] += D
diff[R] -= D
count = 0
cumulative = 0
for i in range(N):
cumulative += diff[i]
if H[i] - cumulative >= 1:
count += 1
print(count)
main()
この解説は claude4.6opus-thinking によって生成されました。