結果
問題 | No.1234 典型RMQ |
ユーザー | flora |
提出日時 | 2022-03-07 17:50:40 |
言語 | PyPy3 (7.3.15) |
結果 |
AC
|
実行時間 | 561 ms / 2,000 ms |
コード長 | 16,323 bytes |
コンパイル時間 | 345 ms |
コンパイル使用メモリ | 82,176 KB |
実行使用メモリ | 105,032 KB |
最終ジャッジ日時 | 2024-07-22 12:56:49 |
合計ジャッジ時間 | 14,115 ms |
ジャッジサーバーID (参考情報) |
judge3 / judge1 |
(要ログイン)
テストケース
テストケース表示入力 | 結果 | 実行時間 実行使用メモリ |
---|---|---|
testcase_00 | AC | 36 ms
52,736 KB |
testcase_01 | AC | 37 ms
52,864 KB |
testcase_02 | AC | 38 ms
52,608 KB |
testcase_03 | AC | 38 ms
52,736 KB |
testcase_04 | AC | 37 ms
52,480 KB |
testcase_05 | AC | 38 ms
52,608 KB |
testcase_06 | AC | 555 ms
104,068 KB |
testcase_07 | AC | 441 ms
94,512 KB |
testcase_08 | AC | 548 ms
104,416 KB |
testcase_09 | AC | 511 ms
101,228 KB |
testcase_10 | AC | 537 ms
104,584 KB |
testcase_11 | AC | 529 ms
103,276 KB |
testcase_12 | AC | 507 ms
100,676 KB |
testcase_13 | AC | 451 ms
95,160 KB |
testcase_14 | AC | 521 ms
99,948 KB |
testcase_15 | AC | 507 ms
98,916 KB |
testcase_16 | AC | 543 ms
104,224 KB |
testcase_17 | AC | 506 ms
99,792 KB |
testcase_18 | AC | 404 ms
92,184 KB |
testcase_19 | AC | 561 ms
105,032 KB |
testcase_20 | AC | 365 ms
100,704 KB |
testcase_21 | AC | 555 ms
104,196 KB |
testcase_22 | AC | 435 ms
98,436 KB |
testcase_23 | AC | 445 ms
99,452 KB |
testcase_24 | AC | 418 ms
98,816 KB |
testcase_25 | AC | 432 ms
99,332 KB |
testcase_26 | AC | 427 ms
98,428 KB |
testcase_27 | AC | 39 ms
52,352 KB |
testcase_28 | AC | 38 ms
52,864 KB |
testcase_29 | AC | 39 ms
52,736 KB |
ソースコード
# ref # [1]https://maspypy.com/segment-tree-%e3%81%ae%e3%81%8a%e5%8b%89%e5%bc%b72#toc4 # [2]https://smijake3.hatenablog.com/entry/2018/11/03/100133 # [3]https://ikatakos.com/pot/programming_algorithm/data_structure/segment_tree/lazy_segment_tree # [4]https://betrue12.hateblo.jp/entry/2020/09/22/194541 # テストケース。以下はテストケースは確認しました。TLEが多いですがどうしようもないです。AOJはpypy3が使えないですが、pypy3が使えるなら通ると思っていいと思います。例えば[t5]は通らないけど同じ問題の[t6]は通っているので。 # 加えて遅延セグ木特有のテストは以下。特にt5はたくさんの基本的問題が含まれていますので、重要です。 # [t5]https://onlinejudge.u-aizu.ac.jp/courses/library/3/DSL/2 TLE # [t6]https://yukicoder.me/problems/no/1234 AC # 実装上の工夫: # 基本的な実装は普通のセグメント木のときと同じになります。つまり[1]を一番参考にしています。ただ[3],[4]についてもアイデアを参考にしている箇所があります。 # lazyは区間長をかけないで管理しています。すなわちlazy[k]はkが管理する区間のひとマスずつの値が入っています。これをtreeに反映するときにlengthをかけます。treeへの反映はmappingでのみ起こって、lengthはmappingの引数で与えています。これは[3]のアイデアです。 # operatorと単位元は2セットあることに注意してください。すなわち、通常のセグ木におけるoperatorとidentityの組と、作用素lazyの演算及びidentityの組です # 例えば最小値を求めるクエリで、区間更新が加算の場合には(operator=min,identity=inf),(lazy_operator=足し算,identity=0)となります。 # 演算が三種類あることは重要です。要素をs,作用素をfとする。sはself.treeの要素で、fはself.lazyの要素と思いましょう。 # 1. s間の演算。これはself.operatorで決めたもの # 2. f間の演算。これは上書きであればfを置き換え、加算であればfの和を求めます。[3]でいうcomposition # 3. 要素sに作用素fを作用させる演算。これは上書きならsをfで置き換え、加算ならsとfの和を求めます。[3]でいうmapping # 実装上は1.は引数で与え、2.,3.はメソッドとして定義してあります # 簡単のために区間加算と区間更新しか定義していないですが、compositionあたりを適切に変えれば他の操作もできるはずです。 # よく使うoperatorと単位元,required_lengthのリスト。 """ 最小値 operator=min, identity=float('inf'),required_length=False 最大値 operator=max, identity = -float('inf'),required_length=False 区間和 operator=lambda x, y: x + y,identity = 0,required_length=True 区間積 operator=lambda x, y: x * y, identity = 1,required_length=True 最大公約数 operator=math.gcd, identity = 0 複数の要素をもたせたいときの区間和。ベクトルの和にする。このときはupdate(k,x)のxにも同じ次元のリストを入れます。 operator=lambda A, B: [a+b for a,b in zip(A,B)] """ #区間加算のみ可能な遅延伝搬セグメント木です。区間更新のみが可能な代わりに区間加算と両方できる遅延伝搬セグメント木よりも定数倍高速に動作します。 #使える場合はこっちを使ってね。 class OnlyAddingLazySegmentTree: # O(N) # 初期化です。適切なoperatorを指定してください。また遅延伝搬セグメント木ではidentityの指定が必須になります。 def __init__(self, A, operator, identity, required_length): """ A:いま扱う配列 operator:いま考える二項間演算子。交換法則は仮定しない identity:operatorに対応する単位元 required_length:区間更新の際に区間長をかけるかどうか。区間和ならTrue,区間の最小値などならFalseにします。 """ # 1-indexedで、親:i>>1,左の子:i<<1,右の子i<<1|1です。 # 要素数 self.N = len(A) # operator,identity self.operator = operator self.identity = identity # 木を成すサイズ2Nの配列を作る。 self.tree = [None] * (self.N + self.N) #加算の場合は上書きフラグが不要なので、加算がない場合は0で初期化するようにして、常に加算すればOKです。 self.lazy = [0]*(self.N+self.N) # 区間長管理専用のリスト。これは__init__で生成されてから今後変更されません。区間長は全部を1にした数列の区間和と考えればよいです。 self.length = [1] * (self.N + self.N) for i, a in enumerate(A, self.N): self.tree[i] = a # ふたつの子にoperatorを作用したものを親にするという操作を繰り返す。ここでindexの大きい方からやっていけば葉から上にたどることができる。 for i in range(self.N - 1, 0, -1): self.tree[i] = self.operator(self.tree[i << 1], self.tree[i << 1 | 1]) # required_lengthがTrueのときは長さをいれて、不要のときはなにもしない(全部1)。 if required_length: self.length[i] = self.length[i << 1] + self.length[i << 1 | 1] # iで指定されたnodeの上を見て、lazyを伝搬させてきます。 def _propagate_from_above(self, i): # i>>hがiの親のindexになります。逆順にして、上から下の順番に計算するようにします。 for h in range(i.bit_length() - 1, 0, -1): # 逆順ループなのでi>>=1みたいにしてはいけません k = i >> h self.tree[k] = self.lazy[k]*self.length[k]+self.tree[k] # lazyを子に伝搬させる。 self.lazy[k << 1] = self.lazy[k << 1]+self.lazy[k] self.lazy[k << 1 | 1] = self.lazy[k << 1 | 1]+self.lazy[k] # 反映させたlazyはリセットする。 self.lazy[k] = 0 # iの上部を再計算します。 def _recalc_above(self, i): while i > 1: i >>= 1 self.tree[i] = self.operator(self.lazy[i<<1]*self.length[i<<1]+self.tree[i<<1],self.lazy[i<<1|1]*self.length[i<<1|1]+self.tree[i<<1|1]) # O(log(N)) # Aのk番目の値A[k]をxに変更します。 def update(self, k, x): # updateの場合はしっかりとlazyをおろした上で考える必要があります。 # lazyを経由しないで直接treeを更新すればいいです。 k += self.N # propagationが必要 self._propagate_from_above(k) # treeを直接更新 self.tree[k] = x # 再計算 self._recalc_above(k) # O(log(N)) # Aのk番目の値A[k]にxを加算します。すなわちA[k]+=xと同じ操作です。 def add(self, k, x): # 加算の場合はlazyは無視してtreeに直接加算すればいいだけ。 k += self.N # 加算 self.tree[k] += x while k > 1: k >>= 1 self.tree[k] = self.operator(self.tree[k << 1], self.tree[k << 1 | 1]) # O(log(N)) # Aの区間[left:right)の部分に一括でxを加算します。 def add_range(self, left, right, x): if left is None: left = 0 if right is None: right = self.N left += self.N right += self.N L0 = left // (left & -left) R0 = right // (right & -right) - 1 # lazyを更新する。クエリと同じような感じです。 while left < right: if left & 1: self.lazy[left] +=x left += 1 if right & 1: right -= 1 self.lazy[right] +=x left >>= 1 right >>= 1 # lazyを計算したら上部の再計算が必要です。これは単にL0,R0からスタートして、親に向かって更新していくだけです。 self._recalc_above(L0) self._recalc_above(R0) # O(log(N)) # 半開区間[left,right)において、operatorで指定された演算の値を返します。 # ただしleft,rightにNoneを入力すると、例えばleft=Noneにするといまありうる一番左を指定します def query(self, left, right): if left is None: left = 0 if right is None: right = self.N # left,rightに対応するtreeのindex left += self.N right += self.N # queryを実行する前に、lazyを上から持ってきます。 self._propagate_from_above(left // (left & -left)) self._propagate_from_above(right // (right & -right) - 1) # 左右の値 vl = self.identity vr = self.identity # 非再帰の実装ではleaf側からrootに向かっていくのがポイントです。[2]の図がわかりやすいです。 # [left,right)の外側から値を確定させていくイメージ。どんどん内側によせていき、leftとrightが重なったら終わり while left < right: if left & 1: # leftが奇数なら、その区間を確定させる。 vl = self.operator(vl, self.lazy[left]*self.length[left]+self.tree[left]) # 次は右の親に移動するのでLを足しておく left += 1 if right & 1: # rightが奇数なら、-1してから確定させる right -= 1 # 順番には注意ね vr = self.operator(self.lazy[right]*self.length[right]+self.tree[right], vr) # 親を見る left >>= 1 right >>= 1 # 最後にvlとvrの演算を返す。ここでも演算の順番に注意 return self.operator(vl, vr) # O(log(N)) # A[k]と同じ。もとのAのk番目の値を取得します。ふつうのセグメント木と違ってO(log(N))なので注意。 def get(self, k): # propagateした上でevaluate_atで取得する必要がある。 k += self.N self._propagate_from_above(k) return self.lazy[k]*self.length[k]+self.tree[k] ############################################## # テストの実行 ############################################## # [t1] """ Q = int(input()) query = [] for _ in range(Q): query.append(tuple(map(int, input().split()))) lst=LazySegmentTree([0]*200001,operator=lambda x,y:x+y,identity=0,required_length=True) for t,x in query: if t==1: #なぜかupdateでも通ったけどaddにするのがただしいと思う lst.add(x,1) else: left=0 right=200001 while left+1<right: mid=left+(right-left)//2 if lst.query(0,mid)>=x: #このときは左による right=mid else: left=mid #leftの削除も必要です。いまの場合削除とは0にすることにほかならないので lst.update(left,0) print(left) """ # [t2] """ N,Q=map(int,input().split()) A=list(map(int,input().split())) query=[] for _ in range(Q): query.append(tuple(map(lambda x:int(x)-1,input().split()))) pos={} for i,a in enumerate(A): pos[a]=i lst=LazySegmentTree(A,operator=min,identity=float('inf'),required_length=False) for flag,l,r in query: if flag==0: #このときl,rを交換する。posも交換しないといけないことに注意 pos[lst.get(l)],pos[lst.get(r)]=pos[lst.get(r)],pos[lst.get(l)] #セグメント木でも交換する work=lst.get(l) lst.update(l,lst.get(r)) lst.update(r,work) else: #最小値は print(pos[lst.query(l,r+1)]+1) """ # [t3] """ N,Q=map(int,input().split()) a=list(map(int,input().split())) query=[] for i in range(Q): #ソートの関係で順番はちょっとかわっている _,l,r,x=map(int,input().split()) query.append((x,l,r,i)) #a[i]がxよりも小さいならそれは和に寄与しないので、入れておく必要がありません。よってa,xを同時に大きい順にソートして答えればいい。 #クエリにaもいれる for i in range(N): query.append((a[i],i)) #ソート query.sort(reverse=True) # 区間和のセグメント木と区間の要素数のセグメント木をどちらも用意します sumlst=LazySegmentTree([0]*N,operator=lambda x,y:x+y,identity=0,required_length=True) numlst=LazySegmentTree([0]*N,operator=lambda x,y:x+y,identity=0,required_length=True) les=[] for q in query: if len(q)==4: x,l,r,i=q #このとき区間の和を求める。和から要素数*xを引きます les.append([i,sumlst.query(l-1,r)-x*numlst.query(l-1,r)]) else: a,i=q #このときはセグメント木をupdate sumlst.update(i,a) numlst.update(i,1) les.sort() for _,d in les: print(d) """ # [t4] """ Q = int(input()) queries = [] X = [] for _ in range(Q): q = tuple(map(int, input().split())) queries.append(q) X.append(q[1]) # 座標圧縮 def coordinate_comp(A, comp_to_original=False): if not comp_to_original: return {a: c for c, a in enumerate(sorted(set(A)))} else: comp_to_A, A_to_comp = {}, {} for c, a in enumerate(sorted(set(A))): comp_to_A[c] = a A_to_comp[a] = c return A_to_comp, comp_to_A x2comp, comp2x = coordinate_comp(X, comp_to_original=True) lst = LazySegmentTree([0] * (len(x2comp) + 1), operator=lambda x, y: x + y, identity=0, required_length=True) # セグメント木の言葉でいうと、まず全要素のうちk番目に小さい要素とは、区間和[0,i]がkになるような最小のiということになります。 # なのでx以上の要素のうち大きい方からk番目の値とは区間和[x,i]がkになるようなiのうち最小の値となります。 for query in queries: if query[0] == 1: lst.add(x2comp[query[1]], 1) elif query[0] == 2: _, x, k = query x = x2comp[x] # 区間和[i,x+1)がkになるようなiのうち最大のiを求める if lst.query(None, x + 1) < k: print(-1) else: left = 0 right = x + 1 while left + 1 < right: mid = left + (right - left) // 2 if lst.query(mid, x + 1) >= k: left = mid else: right = mid # leftが答えです print(comp2x[left]) else: _, x, k = query x = x2comp[x] # 区間和[x,i]がkになるような最小のiを二分探索します if lst.query(x, None) < k: # このとき存在しない print(-1) else: left = x right = len(x2comp) + 1 while left + 1 < right: mid = left + (right - left) // 2 if lst.query(x, mid) >= k: # このときは左による right = mid else: left = mid # leftが答えです print(comp2x[left]) """ # [t5]RMQ """ n,q=map(int,input().split()) queries=[] for _ in range(q): queries.append(list(map(int,input().split()))) lst=LazySegmentTree([(1<<31)-1]*n,operator=min,identity=float('inf'),required_length=False) for q in queries: if q[0]==0: _,i,x=q lst.update(i,x) else: _,s,t=q print(lst.query(s,t+1)) """ # [t5]RSQ """ n,q=map(int,input().split()) queries=[] for _ in range(q): queries.append(list(map(int,input().split()))) lst=LazySegmentTree([0]*n,operator=lambda x,y:x+y,identity=0,required_length=True) for q in queries: if q[0]==0: _,i,x=q lst.add(i-1,x) else: _,s,t=q print(lst.query(s-1,t)) """ # [t5]RUQ """ n,q=map(int,input().split()) queries=[] for _ in range(q): queries.append(list(map(int,input().split()))) lst=LazySegmentTree([(1<<31)-1]*n,operator=min,identity=float('inf'),required_length=False) for q in queries: if q[0]==0: _,s,t,x=q lst.update_range(s,t+1,x) else: _,i=q print(lst.get(i)) """ # [t5]RAQ """ n,q=map(int,input().split()) queries=[] for _ in range(q): queries.append(list(map(int,input().split()))) lst=LazySegmentTree([0]*n,operator=min,identity=float('inf'),required_length=False) for q in queries: if q[0]==0: _,s,t,x=q lst.add_range(s-1,t,x) else: _,i=q print(lst.get(i-1)) """ # [t5]RSQ and RAQ """ n,q=map(int,input().split()) queries=[] for _ in range(q): queries.append(list(map(int,input().split()))) lst=OnlyAddingLazySegmentTree([0]*n,operator=lambda x,y:x+y,identity=0,required_length=True) for q in queries: if q[0]==0: _,s,t,x=q lst.add_range(s-1,t,x) else: _,s,t=q print(lst.query(s-1,t)) """ # [t5]RMQ and RAQ """ n,q=map(int,input().split()) queries=[] for _ in range(q): queries.append(list(map(int,input().split()))) lst=OnlyAddingLazySegmentTree([0]*n,operator=min,identity=float('inf'),required_length=False) for q in queries: if q[0]==0: _,s,t,x=q lst.add_range(s,t+1,x) else: _,s,t=q print(lst.query(s,t+1)) """ # [t6] N=int(input()) a=list(map(int,input().split())) Q=int(input()) queries=[] for _ in range(Q): queries.append(list(map(int,input().split()))) lst = OnlyAddingLazySegmentTree(a, operator=min, identity=float('inf'),required_length=False) for q in queries: if q[0]==1: _,l,r,c=q lst.add_range(l-1,r,c) else: _,l,r,_=q print(lst.query(l-1,r))