公式
E - 気温の変動調査 / Temperature Fluctuation Survey 解説 by admin
GPT 5.2 High概要
各クエリで区間 \([L, R]\) の「最大値 − 最小値」を求める問題です。多数の区間問い合わせを高速に処理するため、セグメント木で区間最小値・最大値を管理します。
考察
各問い合わせごとに \(A_L, A_{L+1}, \dots, A_R\) を走査して最大・最小を求めると、1 回あたり \(O(R-L+1)\) かかります。最悪の場合、各クエリが長い区間になるため
- 素朴解法: \(O(NQ)\)(最大で \(10^5 \times 10^5 = 10^{10}\))となり、時間制限に間に合いません。
ここで重要なのは、求めたい量が
- 区間の最小値
- 区間の最大値
という「区間集約(区間の情報をまとめる)」であり、これらは結合(左右の区間の結果から全体の結果が作れる)できることです。
例えば区間を左右に分けたとき、
- 全体の最小値 = \(\min(\text{左の最小値}, \text{右の最小値})\)
- 全体の最大値 = \(\max(\text{左の最大値}, \text{右の最大値})\)
この性質を利用すると、前処理をして各クエリを \(O(\log N)\) で処理できます。
アルゴリズム
セグメント木(Segment Tree)を使って、各区間の最小値と最大値を同時に管理します。
1. 構築(前処理)
- 要素数 \(N\) 以上の最小の \(2\) 冪を
sizeとします(葉を揃えるため)。 - セグメント木の葉(配列の後半)に \(A_i\) を入れます。
- 下から上へ、各ノードに以下を計算して格納します。
seg_min[i] = min(seg_min[2i], seg_min[2i+1])seg_max[i] = max(seg_max[2i], seg_max[2i+1])
これで任意区間の最小・最大を対数時間で求められます。
2. クエリ処理(区間 \([L, R]\))
実装では扱いやすいように 半開区間 \([l, r)\) に変換します(\(l=L-1\), \(r=R\))。 セグメント木の典型的な走査で、区間を覆うノードだけを拾っていきます。
mn(最小値)を \(+\infty\) で初期化mx(最大値)を \(-\infty\) で初期化- 区間に対応するノードを集めながら
mn,mxを更新 - 最後に答えは
mx - mn
この方法により、1 クエリあたり木の高さ分(\(O(\log N)\))しか見ません。
計算量
- 時間計算量: 構築 \(O(N)\)、各クエリ \(O(\log N)\)、合計 \(O(N + Q\log N)\)
- 空間計算量: \(O(N)\)(最小値用・最大値用にセグ木配列を持つ)
実装のポイント
- 半開区間 \([l, r)\) にすると、セグメント木の反復クエリが書きやすくバグが減ります(コードでも
r = next(it)を「exclusive」として扱っています)。 - 最小・最大を別々の配列(
seg_min,seg_max)で持つと実装が単純です。 - 入力が大きいので、
sys.stdin.buffer.read()で一括読み込みして高速化しています。 - 初期値として
INF = 10**18、-INFを使い、\(A_i\) の範囲(\(\pm 10^9\))より十分大きくしています。
ソースコード
import sys
def main():
data = list(map(int, sys.stdin.buffer.read().split()))
it = iter(data)
N = next(it)
Q = next(it)
A = [next(it) for _ in range(N)]
size = 1
while size < N:
size <<= 1
INF = 10**18
seg_min = [INF] * (2 * size)
seg_max = [-INF] * (2 * size)
for i, v in enumerate(A):
seg_min[size + i] = v
seg_max[size + i] = v
for i in range(size - 1, 0, -1):
seg_min[i] = seg_min[i << 1] if seg_min[i << 1] < seg_min[i << 1 | 1] else seg_min[i << 1 | 1]
seg_max[i] = seg_max[i << 1] if seg_max[i << 1] > seg_max[i << 1 | 1] else seg_max[i << 1 | 1]
out = []
for _ in range(Q):
l = next(it) - 1
r = next(it) # exclusive
l += size
r += size
mn = INF
mx = -INF
while l < r:
if l & 1:
vmin = seg_min[l]
vmax = seg_max[l]
if vmin < mn: mn = vmin
if vmax > mx: mx = vmax
l += 1
if r & 1:
r -= 1
vmin = seg_min[r]
vmax = seg_max[r]
if vmin < mn: mn = vmin
if vmax > mx: mx = vmax
l >>= 1
r >>= 1
out.append(str(mx - mn))
sys.stdout.write("\n".join(out))
if __name__ == "__main__":
main()
この解説は gpt-5.2-high によって生成されました。
投稿日時:
最終更新: