セグメント木メニューのサムネイル
(問題 10)Segment Treeの総まとめ VB(Beta)編(paizaランク S 相当)

問題にチャレンジして、ユーザー同士で解答を教え合ったり、コードを公開してみよう!

問題

下記の問題をプログラミングしてみよう!

(はじめに)

問題 9 まででSegment Treeを一通り作成することが出来ました。


class SegTree():
def calc(self,a, b): #行いたい処理
return max(a, b)

def e(self): #単位元
return 0

def __init__(self, n, A):
#完全2分木を作る
#N = 2 ** k ≧ n となるようにする。(葉の数がnより多くなるようにする。)
self.k = (n - 1).bit_length() #bit_lengthメソッドにより取得が出来る。
N = 2 ** (self.k)
self.N = N
self.Tree = [self.e()] * (2 * N - 1)
#葉の部分に初期要素を入れる
for i in range(n):
self.Tree[N - 1 + i] = A[i]
#葉から近い順にそれぞれの葉以外の頂点の計算を行う
for i in range(N - 2, -1, -1):
self.Tree[i] = self.calc(self.Tree[2 * i + 1], self.Tree[2 * i + 2]) #頂点iの子は2i+1,2i+2、そこの2つを用いて計算を行う

def renewal(self, ind, x): #A[ind]をxに更新
T = self.N - 1 + ind #A[ind]に対応する場所
self.Tree[T] = x
while T > 0: #根にたどるまで更新を続ける
T = (T - 1) // 2
self.Tree[T] = self.calc(self.Tree[2 * T + 1], self.Tree[2 * T + 2])

def product(self, L, R): #A[L]からA[R]までの計算結果を返す
ans = self.e() #答え、単位元で初期化
p = L
while p <= R: #出来るだけ貪欲にlからrまで進める
m = p
cnt = 0 #何回 2 で割れるか計算する。
#ここの演算を適当なものに変えることで計算量が少し改善する
while m % 2 == 0:
if m == 0: #0のときだけ無限に割れてしまうため例外処理をする。
cnt = 10 ** 9
break
cnt += 1
m = m // 2
cnt2 = (R - p + 1).bit_length() - 1
l = min(cnt, cnt2) #SegTreeの性質上、lから始まるものの最大幅は2 ** cntである。範囲を飛び出してしまうため2 ** cnt2 より大きくは進めない。
#pからp+2**l-1までの計算結果が入っている配列の場所を探す
kdash = self.k - l
ans = self.calc(ans, self.Tree[(2 ** kdash - 1) + p // 2 ** l]) #(2 ** kdash - 1) で幅 2 ** l の一番左の配列のindexをとり、そこからp // 2 ** l の分だけずれる
p += 2 ** l
return ans


では、最後にここまでの総まとめをしましょう。

(問題)

長さが n の配列 A が与えられます。Q 個のクエリが与えられるので順番に処理を行ってください。i 番目のクエリは以下のどちらかです。

  • q_i = 1 のとき A_{a_i},A_{a_i + 1},...,A_{b_i} の中で最も大きい整数を出力する。

  • q_i = 2 のとき A_{a_i} を b_i に変更する。
  • 入力される値

    入力は以下のフォーマットで与えられます。


    n
    A_0 A_1 ... A_{n - 1}
    Q
    q_1 a_1 b_1
    q_2 a_2 b_2
    ...
    q_Q a_Q b_Q


    1 行目には 1 つの整数 n が与えられます。

    2 行目には n 個の整数 A_0,A_1,...,A{n - 1} が与えられます。

    3 行目には 1 つの整数 Q が与えられます。

    3 + i (1 ≦ i ≦ Q) 行目には 3 つの整数 q_i,a_i,b_i が与えられます。

    入力は合計 3 + Q 行からなり、入力値最終行の末尾に改行を 1 つ含みます。


    入力値最終行の末尾に改行が1つ入ります。
    文字列は標準入力から渡されます。 標準入力からの値取得方法はこちらをご確認ください
    期待する出力

    q_i = 1 となる i の個数を q としたとき答えを q 行で出力してください。

    j (1 ≦ j ≦ q) 行目には q_i = 1 となるクエリの中で j 番目のクエリの答えを出力してください。

    条件

    すべてのテストケースについて以下の条件を満たします。

  • 1 ≦ n ≦ 100000

  • 0 ≦ A_i ≦ 10 ^ 9 (0 ≦ i ≦ n - 1)

  • 1 ≦ Q ≦ 100000

  • 1 ≦ q_i ≦ 2 (1 ≦ i ≦ Q)

  • q_i = 1 のとき 0 ≦ a_i ≦ b_i ≦ n - 1 (1 ≦ i ≦ Q)

  • q_i = 2 のとき 0 ≦ a_i ≦ n - 1, 0 ≦ b_i ≦ 10 ^ 9 (1 ≦ i ≦ Q)

  • q_i = 1 となる i が 1 つは存在する
  • 入力例1

    5
    1 5 7 9 8
    7
    1 1 3
    1 1 2
    2 3 0
    2 1 100
    1 1 3
    1 3 3
    1 2 3

    出力例1

    9
    7
    100
    0
    7

    問題一覧へ戻る

    1. paizaラーニングトップ
    2. レベルアップ問題集
    3. セグメント木メニュー(言語選択)
    4. 問題一覧 VB(Beta)編
    5. (問題 10)Segment Treeの総まとめ VB(Beta)編
    ページの先頭へ戻る