E - 山の高さ調査 / Mountain Height Survey Editorial by admin
GPT 5.2 High概要
配列 \(A\) に対して、各クエリ \((L,R)\) の区間最大値 \(\max(A_L,\dots,A_R)\) を高速に答える問題です。多数の区間最大クエリを処理するため、セグメント木を使います。
考察
各問い合わせごとに、区間 \([L,R]\) を先頭から走査して最大値を求めると、1クエリあたり \(O(R-L+1)\) かかります。最悪の場合は \(O(N)\) となり、全体では \(O(NQ)\) です。
制約は \(N,Q \le 10^5\) なので、例えば \(N=Q=10^5\) だと \(10^{10}\) 回程度の操作になり、これは現実的な時間では間に合いません(TLE)。
そこで、「配列を前処理して、各区間の最大値を対数時間で答える」必要があります。区間最大のような演算(結合則が成り立つ:\(\max(\max(a,b),c)=\max(a,\max(b,c))\))は、セグメント木で扱えます。
アルゴリズム
セグメント木(区間最大)
セグメント木は、配列を区間に分割して管理する完全二分木です。
- 葉:各要素 \(A_i\)
- 内部ノード:そのノードが担当する区間の最大値
1. 構築(前処理)
要素数 \(N\) 以上の最小の \(2\) の冪を size とし、配列を木の葉(seg[size + i])に置きます。残りの葉は「最大値に影響しない十分小さい値(\(-\infty\))」で埋めます。
その後、下から順に
- seg[i] = max(seg[2*i], seg[2*i+1])
として内部ノードを作れば、根 seg[1] まで最大値が埋まります。
2. クエリ処理(区間 \([L,R]\) の最大)
クエリ \((L,R)\) は 1-indexed で与えられるので、0-indexに直してから葉の位置へ移します:
- l = (L-1) + size
- r = (R-1) + size
そして次の要領で区間をカバーする最小限のノードだけを集めます:
lが右子(奇数)なら、そのノードは区間に完全に含まれるので答えに反映してl++rが左子(偶数)なら、そのノードも区間に完全に含まれるので答えに反映してr--- その後
l//=2, r//=2で親へ上がる
これを l > r になるまで繰り返すと、ちょうど区間 \([L,R]\) を覆うノード集合の最大値が求まります。各ステップで高さ分だけ上がるため、計算量は \(O(\log N)\) です。
(例)\([L,R]\) を「2の冪長の区間」に分解して、その最大を取るイメージです。セグメント木はこの分解を自動でやってくれます。
計算量
- 時間計算量: 構築 \(O(N)\)、各クエリ \(O(\log N)\)、合計 \(O(N + Q\log N)\)
- 空間計算量: セグメント木配列に \(O(\text{size}) = O(N)\)
実装のポイント
1-indexed入力に注意:クエリの \(L,R\) は 1-indexed なので、
L-1,R-1に直してから処理します。sizeは \(2\) の冪にする:セグメント木を配列で管理するとき、sizeを \(2\) の冪にすると親子関係(iの子が2i,2i+1)が簡単になります。初期値(単位元):最大値用なので、初期値は十分小さい値(コードでは
-10**18)を使います。高速入出力:\(Q\) が大きいので、
sys.stdin.readlineと、出力を配列に貯めて最後にまとめて出す方法が有効です。ソースコード
import sys
def main():
input = sys.stdin.readline
N, Q = map(int, input().split())
A = list(map(int, input().split()))
size = 1
while size < N:
size <<= 1
seg = [-10**18] * (2 * size)
for i, v in enumerate(A):
seg[size + i] = v
for i in range(size - 1, 0, -1):
left = seg[i * 2]
right = seg[i * 2 + 1]
seg[i] = left if left > right else right
out = []
for _ in range(Q):
l, r = map(int, input().split())
l = l - 1 + size
r = r - 1 + size
res = -10**18
while l <= r:
if l & 1:
if seg[l] > res:
res = seg[l]
l += 1
if not (r & 1):
if seg[r] > res:
res = seg[r]
r -= 1
l >>= 1
r >>= 1
out.append(str(res))
sys.stdout.write("\n".join(out))
if __name__ == "__main__":
main()
この解説は gpt-5.2-high によって生成されました。
posted:
last update: