結果
問題 | No.875 Range Mindex Query |
ユーザー |
|
提出日時 | 2023-07-20 15:04:22 |
言語 | PyPy3 (7.3.15) |
結果 |
AC
|
実行時間 | 880 ms / 2,000 ms |
コード長 | 7,432 bytes |
コンパイル時間 | 382 ms |
コンパイル使用メモリ | 82,176 KB |
実行使用メモリ | 96,384 KB |
最終ジャッジ日時 | 2024-09-20 11:19:26 |
合計ジャッジ時間 | 9,002 ms |
ジャッジサーバーID (参考情報) |
judge4 / judge3 |
(要ログイン)
ファイルパターン | 結果 |
---|---|
sample | AC * 1 |
other | AC * 18 |
ソースコード
#!/usr/bin/env python3import syssys.setrecursionlimit(10 ** 9)class SegTree:def __init__(self, unit: int, bottomList: "list[int]", func: "function", isLogging: bool = False, convertLengthToThePowerOf2: bool = False):self.unit = unitself.func = funcself.bottomLen = self._getSegLenOfThePowerOf2(len(bottomList)) if convertLengthToThePowerOf2 else len(bottomList)self.actualLen = len(bottomList)self.offset = self.bottomLen # セグ木の最下層の最初のインデックスに合わせるためのオフセットself.segLen = self.bottomLen * 2self.tree = [unit] * self.segLenself.isLogging = isLoggingif self.isLogging:self.logtree = [len(str(unit)) + 2] * self.segLenself._build(bottomList)def _build(self, seq):"""初期化O(self.segLen)"""# 最下段の初期化for i, x in enumerate(seq, self.offset):self.tree[i] = xif self.isLogging:self.logtree[i] = len(str(x)) + 2# ビルドfor i in range(self.offset - 1, 0, -1):self.tree[i] = self.func(self.tree[i << 1], self.tree[i << 1 | 1])if self.isLogging:self.logtree[i] = max(len(str(self.tree[i])) + 2, self.logtree[i << 1] + self.logtree[i << 1 | 1] + 1)def _getSegLenOfThePowerOf2(self, ln: int):"""直近の2べきの長さを算出"""if ln <= 0:return 1else:import mathdecimalPart, integerPart = math.modf(math.log2(ln))return 2 ** (int(integerPart) + (0 if decimalPart == float(0) else 1))def pointAdd(self, i: int, val: int):"""一点加算 他演算O(log(self.bottomLen))"""i += self.offsetself.tree[i] += valif self.isLogging:self.logtree[i] = len(str(self.tree[i])) + 2# self.tree[i] = self.func(self.tree[i], val) <- こっちの方が都度の修正は発生しない。再帰が遅くないか次第。while i > 1:i >>= 1 # 2で割って頂点に達するまで下層から遡上self.tree[i] = self.func(self.tree[i << 1], self.tree[i << 1 | 1]) # 必ず末尾0と1がペアになるのでor演算子if self.isLogging:self.logtree[i] = max(len(str(self.tree[i])) + 2, self.logtree[i << 1] + self.logtree[i << 1 | 1] + 1)def pointUpdate(self, i: int, val: int):"""一点更新O(log(self.bottomLen))"""i += self.offsetself.tree[i] = valif self.isLogging:self.logtree[i] = len(str(self.tree[i])) + 2while i > 1:i >>= 1 # 2で割って頂点に達するまで下層から遡上self.tree[i] = self.func(self.tree[i << 1], self.tree[i << 1 | 1]) # 必ず末尾0と1がペアになるのでor演算子if self.isLogging:self.logtree[i] = max(len(str(self.tree[i])) + 2, self.logtree[i << 1] + self.logtree[i << 1 | 1] + 1)def getRange(self, l: int, r: int):"""区間取得 (l ≦ X < r)l ~ r-1までの区間 (0-indexed)。※右端を含まない。O(log(self.bottomLen))"""l += self.offsetr += self.offsetvL = self.unitvR = self.unitwhile l < r:if l & 1:vL = self.func(vL, self.tree[l])l += 1if r & 1:r -= 1vR = self.func(self.tree[r], vR)l >>= 1r >>= 1return self.func(vL, vR)def getPoint(self, i: int):"""一点取得O(1)"""i += self.offsetreturn self.tree[i]def max_right(self, l, is_ok: "function"):"""二分探索O(log(self.bottomLen))※ セグ木上の二分探索をする場合は2べきにすること。# !!!! ng側が返却される !!!!!"""print("セグ木上の二分探索をする場合は2べきにすること。 convertLengthToThePowerOf2=True")l += self.offsetidx = l // (l & -l) # lから始まる最も大きいセグメントのインデックス算出。(= 2で割れなくなるまで割る)ans = self.unitwhile is_ok(self.func(ans, self.tree[idx])): # そのセグメントが条件を満たすかどうかの判定# 条件を満たす限り上へとより範囲を広げていく。ans = self.func(ans, self.tree[idx])idx += 1idx //= (idx & -idx)if idx == 1: # 最上層まで到達したら全範囲満たすということ。 → (2べきになるようにモノイド埋めする前の)実際の長さを返す。return self.actualLenwhile idx < self.offset:# 下へと降りていき境界値を見つける。idx <<= 1 # 一階層下のセグメント(左側)へ移動 (=2倍)## | idx |# | idx<<1 | idx<<1 + 1 |#if is_ok(self.func(ans, self.tree[idx])): # 条件を満たすなら同一階層の右側のセグメントの下層(左側)へ。満たさないならそのまま下層(左側)へ。ans = self.func(ans, self.tree[idx])idx += 1return idx - self.offset - 1# 未検証def min_left(self, r, is_ok):r += self.offsetrr = max(r // (~r & -~r), 1)ans = self.unitwhile is_ok(self.func(self.tree[rr], ans)):ans = self.func(self.tree[rr], ans)rr -= 1while rr & 1:rr >>= 1if rr == 0:return -1while rr < self.offset:rr <<= 1if is_ok(self.func(self.tree[rr+1], ans)):ans = self.func(self.tree[rr+1], ans)else:rr += 1return rr - self.offsetdef __str__(self) -> str:if not self.isLogging:return "[" + ", ".join([str(i) for i in self.tree]) + "]"res = []PowerOf2Set = set([2 ** i for i in range(8)]) # どうぜログ出力で確認できるのはせいぜいこの辺までfor i in range(1, self.segLen):if i in PowerOf2Set:res.append("\n|")res.append(str(self.tree[i]).center(self.logtree[i], " "))res.append("|")return "".join(res)def main():N, Q = map(int, input().split())A = list(map(int, input().split()))def f(a, b):if a[0] < b[0]:return areturn bINF = 10 ** 9seg = SegTree(unit=(INF, -1), bottomList=[(A[i], i) for i in range(N)], func=f, isLogging=True)for _ in range(Q):t, *args = map(int, input().split())if t == 1:l, r = args[0] - 1, args[1] - 1al, ar = seg.getPoint(l)[0], seg.getPoint(r)[0]seg.pointUpdate(l, (ar, l))seg.pointUpdate(r, (al, r))else:l, r = args[0] - 1, args[1] - 1print(seg.getRange(l, r + 1)[1] + 1)returnif __name__ == '__main__':main()