Official
A - 温度管理と収穫判定 / Water Management and Harvest Value Aggregation Editorial by admin
Gemini 3.0 Flash (Thinking)概要
\(N\) 個の区画の「収穫価値」と「水分量」を管理し、範囲への加算、区画の閉鎖、そして特定の条件(水分量が \(0\) 以下)を満たす区画の価値の合計を求める問題です。
考察
この問題の核心は、クエリごとに最大 \(N\) 個の区画を走査する必要がある点にあります。
- 制約の確認: \(N\) と \(Q\) はともに最大 \(3000\) です。単純なシミュレーションを行うと、計算量は最大で \(O(N \times Q)\) となり、操作回数は \(3000 \times 3000 = 9 \times 10^6\) 回程度になります。
- 計算量の見積もり: 一般的に、1秒間に処理できる計算量は \(10^7\) 〜 \(10^8\) 回程度と言われています。Pythonの場合、通常の
forループは低速ですが、リストのスライスや組み込み関数を適切に使うことで、この程度の計算量であれば制限時間内に処理することが可能です。 - 閉鎖された区画の扱い: 区画が閉鎖された場合、それ以降の計算から除外する必要があります。リストから要素を削除してしまうと、番号(インデックス)がずれてしまうため、「存在しないものとみなす」工夫が必要です。具体的には、収穫価値 \(S_i\) を \(0\) にし、水分量 \(C_i\) を絶対に \(0\) 以下にならないような大きな値に書き換えることで、条件判定と合計値の両方に影響を与えないようにできます。
アルゴリズム
各操作を愚直にシミュレーションする「計算幾何」や「セグメント木」などの高度なデータ構造を使わないアプローチをとります。
- 初期化: 各区画の収穫価値 \(S\) と水分量 \(C\) を配列(リスト)で保持します。
- 操作 1 (範囲加算): 指定された範囲 \([l, r]\) の \(C_i\) に対して \(v\) を加算します。Pythonではスライスを用いて
C[l:r+1] = [c + v for c in C[l:r+1]]と書くことで高速に処理できます。 - 操作 2 (区画閉鎖): 指定された区画 \(x\) を無効化します。
- \(S_x = 0\) とする。
- \(C_x = \infty\) (非常に大きな数)とする。
- 操作 3 (条件付き合計): 範囲 \([l, r]\) を走査し、 \(C_i \leq 0\) である区画の \(S_i\) を合計します。
計算量
- 時間計算量: \(O(NQ)\)
- 各クエリに対して最大 \(N\) 要素の走査や更新を行うため、全体の計算量は \(O(N \times Q)\) となります。\(N, Q \leq 3000\) では最大 \(9 \times 10^6\) であり、Pythonの高速な記法を用いることで十分に実行時間制限に間に合います。
- 空間計算量: \(O(N)\)
- 区画の情報を保持するための配列のサイズは \(N\) に比例します。
実装のポイント
高速な入出力: \(Q\) が大きいため、
input()を繰り返すのではなくsys.stdin.read().split()で一括で読み込み、sys.stdout.writeで一括で出力することで実行時間を短縮しています。Pythonの高速化テクニック:
- スライスとリスト内包表記:
C[l:r+1] = [c + v for c in C[l:r+1]]は、通常のforループで1要素ずつ更新するよりも内部的に最適化されており高速です。 - zip関数の利用: 操作 3 で
CとSの両方の値を参照する際、zip(sub_C, sub_S)を使うことでインデックスによるアクセス(sub_C[i])を避け、より高速にループを回すことができます。
- スライスとリスト内包表記:
閉鎖の定数処理: 操作 2 で \(C_x\) に代入する値は、操作 1 で \(Q\) 回 \(10^4\) ずつ減らされたとしても \(0\) 以下にならないよう、\(10^9\) などの十分に大きな値にする必要があります。
ソースコード
import sys
def solve():
# 標準入力からすべてのデータを一括で読み込み、スペース区切りで分割します。
# これにより、大量の入力データに対しても高速に処理を行うことができます。
input_data = sys.stdin.read().split()
if not input_data:
return
# N: 区画の数, Q: 操作の回数
N = int(input_data[0])
Q = int(input_data[1])
# S: 各区画の収穫価値, C: 各区画の初期水分量
S = [int(x) for x in input_data[2:N+2]]
C = [int(x) for x in input_data[N+2:2*N+2]]
# クエリ部分のデータを取得します。
q_data = input_data[2*N+2:]
results = []
idx = 0
# Q回の操作を順番に処理します。
# Pythonのリスト操作(スライスやリスト内包表記)はC言語レベルで最適化されているため、
# 5000x5000程度の計算量であれば時間内に処理が可能です。
for _ in range(Q):
if idx >= len(q_data):
break
t = q_data[idx]
if t == '1':
# 操作 1: 水分量の加算
l = int(q_data[idx+1]) - 1
r = int(q_data[idx+2]) - 1
v = int(q_data[idx+3])
if v != 0:
# リスト内包表記を用いたスライスへの代入は、通常のforループより高速です。
C[l:r+1] = [c + v for c in C[l:r+1]]
idx += 4
elif t == '2':
# 操作 2: 区画の閉鎖
x = int(q_data[idx+1]) - 1
# 閉鎖された区画は、収穫価値を0にし、水分量を非常に大きな値に設定することで、
# 以降の問い合わせにおいて条件を満たさない(かつ合計に影響を与えない)ようにします。
S[x] = 0
C[x] = 10**9 # 5000回の操作で減少しきらない十分大きな値
idx += 2
elif t == '3':
# 操作 3: 収穫価値の合計
l = int(q_data[idx+1]) - 1
r = int(q_data[idx+2]) - 1
total = 0
# スライスとzipを組み合わせることで、インデックスによるアクセスを減らし、
# ループの実行速度を向上させます。
sub_C = C[l:r+1]
sub_S = S[l:r+1]
for ci, si in zip(sub_C, sub_S):
if ci <= 0:
total += si
results.append(str(total))
idx += 3
# すべての結果をまとめて出力します。
if results:
sys.stdout.write('\n'.join(results) + '\n')
if __name__ == '__main__':
solve()
この解説は gemini-3-flash-thinking によって生成されました。
posted:
last update: