C - 水やり当番 / Watering Duty Editorial by admin
Qwen3-Coder-480B概要
\(N\) 個のプランターに、\(Q\) 回の操作で指定された区間に水をあげるとき、最終的に各プランターに与えられた水の総量を求める問題。
考察
この問題では、各クエリで「ある区間のすべての要素に同じ値を加算する」という操作が繰り返されます。例えば、プランター \(L\) から \(R\) までに \(C\) リットルずつ水をやるという操作は、配列の添字 \(L-1\) から \(R-1\) までの要素すべてに \(C\) を足す操作に対応します(0-indexedに変換)。
素朴な方法として、「各クエリごとに実際に区間に対して値を足していく」ことが考えられます。しかし、この方法では最悪の場合、1回のクエリで最大 \(N\) 個の要素を更新する必要があり、全体で \(O(NQ)\) の計算量がかかります。制約が \(N, Q \leq 2 \times 10^5\) であるため、これは時間内に処理できません(\(10^{10}\) オーダーの計算が必要)。
そこで登場するのが「差分配列(いもす法)」というテクニックです。差分配列を使うことで、区間への一様加算を定数時間で処理できます。
たとえば、配列 \(A\) の区間 \([L, R]\) すべてに値 \(C\) を加える操作は、差分配列 \(diff\) を用いて以下のように表現できます: - \(diff[L] \mathrel{+}= C\) - \(diff[R+1] \mathrel{-}= C\)
そして最後に、\(diff\) の累積和を取ることで、元の配列を得ることができます。
この方法を使えば、各クエリを \(O(1)\) で処理でき、最後に累積和を取るステップが \(O(N)\) なので、全体で \(O(N + Q)\) で解くことができます。
アルゴリズム
- 配列
diffをサイズ \(N+1\) で用意する(1-indexedで扱うため、境界処理のために1つ多く確保)。 - 各クエリ \((L_i, R_i, C_i)\) に対して、以下のように差分を記録する:
diff[L_i - 1] += C_idiff[R_i] -= C_i
diffの累積和を取り、各プランターに与えられた水の量を計算する。
計算量
- 時間計算量: \(O(N + Q)\)
- 空間計算量: \(O(N)\)
実装のポイント
- 入力を高速に読み込むために
sys.stdin.read()を使用している(Pythonでは重要)。 - 差分配列の添字管理に注意:1-indexedの入力を0-indexedに変換する。
- 最後に累積和を取るときに、結果をリストに保存して一度に空白区切りで出力すると効率的。
## ソースコード
```python
import sys
input = sys.stdin.read
def main():
data = input().split()
N = int(data[0])
Q = int(data[1])
# 差分配列を用意
diff = [0] * (N + 1)
index = 2
for _ in range(Q):
L = int(data[index])
R = int(data[index+1])
C = int(data[index+2])
index += 3
# 1-indexed なので、0-indexed に変換
diff[L-1] += C
diff[R] -= C
# 差分配列から元の配列を構築
result = []
current = 0
for i in range(N):
current += diff[i]
result.append(current)
print(' '.join(map(str, result)))
if __name__ == "__main__":
main()
この解説は qwen3-coder-480b によって生成されました。