#はじめに
他でもたくさんセグメント木の記事を見かけるので、皆さん使いやすいものを選んでください!(この記事のセグメント木が気に入った方は是非是非、競技プログラミング等で使ってください。大歓迎です。)
#参考記事
https://algo-logic.info/segment-tree/
https://qiita.com/takayg1/items/c811bd07c21923d7ec69
https://qiita.com/mosamosa/items/d17ab5af6e19f67202cb
#コードの紹介
今日紹介するのはmodeでセグメント木の関数を選べるようにしたものです。以下コード
(2021/1/13コメントを受けif羅列→辞書に修正)
(2021/8/12若干の修正。(__iter__等の追加)
import math
class SegTree:
DEFAULT = {
'min': 1 << 60,
'max': -(1 << 60),
'sum': 0,
'prd': 1,
'gcd': 0,
'lmc': 1,
'^': 0,
'&': (1 << 60) - 1,
'|': 0,
}
FUNC = {
'min': min,
'max': max,
'sum': (lambda x, y: x + y),
'prd': (lambda x, y: x * y),
'gcd': math.gcd,
'lmc': (lambda x, y: (x * y) // math.gcd(x, y)),
'^': (lambda x, y: x ^ y),
'&': (lambda x, y: x & y),
'|': (lambda x, y: x | y),
}
def __init__(self, ls, mode='min', func=None, default=None):
"""
要素ls, 関数mode (min,max,sum,prd(product),gcd,lmc,^,&,|)
func,defaultを指定すれば任意の関数、単位元での計算が可能
"""
N = len(ls)
if default == None:
self.default = self.DEFAULT[mode]
else:
self.default = default
if func == None:
self.func = self.FUNC[mode]
else:
self.func = func
self.N = N
self.K = (N - 1).bit_length()
self.N2 = 1 << self.K
self.dat = [self.default] * (2**(self.K + 1))
for i in range(self.N): # 葉の構築
self.dat[self.N2 + i] = ls[i]
self.build()
def build(self):
for j in range(self.N2 - 1, -1, -1):
self.dat[j] = self.func(self.dat[j << 1], self.dat[j << 1 | 1]) # 親が持つ条件
def leafvalue(self, x): # リストのx番目の値
return self.dat[x + self.N2]
def update(self, x, y): # index(x)をyに変更
i = x + self.N2
self.dat[i] = y
while i > 0: # 親の値を変更
i >>= 1
self.dat[i] = self.func(self.dat[i << 1], self.dat[i << 1 | 1])
return
def query(self, L, R): # [L,R)の区間取得
L += self.N2
R += self.N2
vL = self.default
vR = self.default
while L < R:
if L & 1:
vL = self.func(vL, self.dat[L])
L += 1
if R & 1:
R -= 1
vR = self.func(self.dat[R], vR)
L >>= 1
R >>= 1
return self.func(vL, vR)
def __iter__(self):
for i in range(self.N):
yield self[i]
def __getitem__(self, x): return self.leafvalue(x)
def __setitem__(self, x, val): return self.update(x, val)
セグメント木に乗せられるモノイドは大体網羅していると思います(min,max,sum,product,gcd,lmc,^,&,|)。間違い等ありましたらご指摘お願いいたします。
#説明
###木の構築
木の構築に必要な引数は初期要素(list),関数(mode)の2つです。(func,defaultを指定することで任意の関数、単位元での計算が可能)単位元に関しては、DEFAULTを定義しています。計算量O(N)
###値の更新
updateで変更するインデックスと変更後の値を指定してください。計算量O(log(N))
###値の取得
leafvalueで任意のインデックスの値を取得できます。計算量O(1)。(例えば、値の更新とくみあわせてk番目の値にxを足すといった操作が可能です。)
###区間の値の取得
queryで区間の値が取得できます。query(L,R)取得できる値は区間[L,R)の値です。計算量O(log(N))
#使用例
1, atcoder ABC185F
https://atcoder.jp/contests/abc185/tasks/abc185_f
https://atcoder.jp/contests/abc185/submissions/19042056
xorのやつです。900msくらい。まだまだ改善の余地ありそうですね、、、
2, atcoder Chokudai SpeedRun 001J
https://atcoder.jp/contests/chokudai_S001/tasks/chokudai_S001_j
https://atcoder.jp/contests/chokudai_S001/submissions/19404893
転倒数を求める問題です。sumが使えます。
3, atcoder Chokudai SpeedRun 001H
https://atcoder.jp/contests/chokudai_S001/tasks/chokudai_S001_h
https://atcoder.jp/contests/chokudai_S001/submissions/19404925
LISの問題です。maxが使えます。
#おわりに
ここまでお読みいただきありがとうございました。私自身競技プログラミング初学者ですので、間違い等ございましたらご指摘よろしくお願いいたします。共に競技プロライフ楽しみましょう!!