結果
問題 | No.877 Range ReLU Query |
ユーザー | flora |
提出日時 | 2022-03-07 20:40:52 |
言語 | PyPy3 (7.3.15) |
結果 |
AC
|
実行時間 | 1,355 ms / 2,000 ms |
コード長 | 8,711 bytes |
コンパイル時間 | 316 ms |
コンパイル使用メモリ | 82,048 KB |
実行使用メモリ | 115,428 KB |
最終ジャッジ日時 | 2024-11-08 10:42:34 |
合計ジャッジ時間 | 14,635 ms |
ジャッジサーバーID (参考情報) |
judge1 / judge4 |
(要ログイン)
テストケース
テストケース表示入力 | 結果 | 実行時間 実行使用メモリ |
---|---|---|
testcase_00 | AC | 43 ms
52,480 KB |
testcase_01 | AC | 80 ms
71,424 KB |
testcase_02 | AC | 77 ms
69,376 KB |
testcase_03 | AC | 102 ms
76,544 KB |
testcase_04 | AC | 56 ms
61,440 KB |
testcase_05 | AC | 62 ms
62,720 KB |
testcase_06 | AC | 67 ms
65,408 KB |
testcase_07 | AC | 63 ms
63,232 KB |
testcase_08 | AC | 108 ms
76,800 KB |
testcase_09 | AC | 59 ms
61,696 KB |
testcase_10 | AC | 73 ms
66,944 KB |
testcase_11 | AC | 1,255 ms
113,708 KB |
testcase_12 | AC | 1,105 ms
109,056 KB |
testcase_13 | AC | 893 ms
101,928 KB |
testcase_14 | AC | 913 ms
104,708 KB |
testcase_15 | AC | 1,336 ms
115,428 KB |
testcase_16 | AC | 1,252 ms
111,728 KB |
testcase_17 | AC | 1,277 ms
112,968 KB |
testcase_18 | AC | 1,282 ms
112,284 KB |
testcase_19 | AC | 1,239 ms
110,920 KB |
testcase_20 | AC | 1,355 ms
114,824 KB |
ソースコード
# ref # [1]蟻本 # [2]http://hos.ac/slides/20140319_bit.pdf # テストケース # [t1]https://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=DSL_2_B&lang=jp # [t2]https://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=ALDS1_5_D&lang=jp # [t3]https://atcoder.jp/contests/abc241/tasks/abc241_d # [t4]https://yukicoder.me/problems/no/877 # [t5]https://atcoder.jp/contests/arc033/tasks/arc033_3 # [t6]https://yukicoder.me/problems/no/877 # BinaryIndexedTreeです。 # 区間和をO(log(N))で求めることができます。また数列に要素を加算することができます。 # 同じことがセグメント木でもできますが、こっちのほうが速いので、区間和を求めたいときはこっちを使いましょう。updateも実はできて、updateしたいときでもセグメント木よりかなりはやいです。 class BinaryIndexedTree: # O(N*log(N)) # 実は初期化はセグメント木より遅い(0じゃない値で初期化する場合には) # ただしAを全部0にすることもでき、その場合にはO(N)で初期化できるように実装してあります。ただしNのループは回るので定数倍だけ[0]で初期化するよりも遅くなりますが……。 def __init__(self, A): """ A: 初期化する配列 """ # 要素数 self.N = len(A) # BITを管理する木、1-indexedにするので要素数はN+1 self.tree = [0] * (self.N + 1) # Aでtreeを初期化します。このとき0ではaddを実行しないようにします。これにより配列が全部0ならO(N)で初期化できます。 for k, a in enumerate(A): if a != 0: self.add(k, a) # 半開区間[0,i)の和を求めます def _sum(self, i): # 半開区間であることと0-indexedであることがうまく相殺して蟻本と完全に同じ実装になる。 # sumです s = 0 while i > 0: s += self.tree[i] i -= i & -i return s # O(log(N)) # a[k]にxを加算します、すなわちa[k]+=xと同じ操作 def add(self, k, x): # self.treeは1-indexedなので1を足しておく必要がある k += 1 while k <= self.N: self.tree[k] += x k += k & -k #O(log(N)) #a[k]をxに変更します。a[k]=x def update(self,k,x): #BITだとストレートにupdateできないが、getしてからaddするという方法でできる。#いまのxを打ち消すような値を追加する self.add(k,x-self.get(k)) # O(log(N)) # 半開区間[left,right)の和を求めます。ただし、区間が空集合のときはErrorを吐きます # ただしleft,rightにNoneを入力すると、例えばleft=Noneにするといまありうる一番左を指定します def sum(self, left, right): # Noneなら持ち変える if left is None: left = 0 if right is None: right = self.N if 0 <= left <= right <= self.N: return self._sum(right) - self._sum(left) else: raise ValueError( "半開区間[{},{})が空集合、もしくはIndex out of rangeです。".format( left, right)) # O(log(N)) # セグメント木と違って定数オーダーではないので注意してください。もとの全部の要素を持ってないので、sumで取り出す必要があるからです。 # これを何回も使う場合はセグメント木を使うべきである。 def get(self, k): return self.sum(k, k + 1) # [t1] """ n,q=map(int,input().split()) query=[] for _ in range(q): query.append(list(map(int,input().split()))) bit=BinaryIndexedTree([0]*n) for com,x,y in query: if com==0: bit.add(x-1,y) else: print(bit.sum(x-1,y)) """ # [t2] """ N=int(input()) a=list(map(int,input().split())) #数列の反転数がBITを用いてO(N*log(N))で求められることは暗記しておきましょう。 #いまmax(a[i])が大きく、そのサイズのbitを用意できないので、座標圧縮する 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 a2comp=coordinate_comp(a) a=[a2comp[ai] for ai in a] #bitは[min(a),max(a)]の範囲を用意する。いまは座標圧縮したので[0,len(a)] bit=BinaryIndexedTree([0]*N) #転倒数 les=0 for j in range(N): les+=j-bit.sum(None,a[j]) bit.add(a[j],1) print(les) """ # [t3] """ 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) bit=BinaryIndexedTree([0]*(len(x2comp)+1)) #セグメント木の言葉でいうと、まず全要素のうちk番目に小さい要素とは、区間和[0,i]がkになるような最小のiということになります。 #なのでx以上の要素のうち大きい方からk番目の値とは区間和[x,i]がkになるようなiのうち最小の値となります。 for query in queries: if query[0]==1: bit.add(x2comp[query[1]],1) elif query[0]==2: _,x,k=query x=x2comp[x] #区間和[i,x+1)がkになるようなiのうち最大のiを求める if bit.sum(None,x+1)<k: print(-1) else: left=0 right=x+1 while left+1<right: mid=left+(right-left)//2 if bit.sum(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 bit.sum(x,None)<k: #このとき存在しない print(-1) else: left=x right=len(x2comp)+1 while left+1<right: mid=left+(right-left)//2 if bit.sum(x,mid)>=k: #このときは左による right=mid else: left=mid #leftが答えです print(comp2x[left]) """ # [t4] """ 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) sumbit=BinaryIndexedTree([0]*N) cntbit=BinaryIndexedTree([0]*N) les=[] for q in query: if len(q)==4: x,l,r,i=q #このとき区間の和を求める。和から要素数*xを引きます sumv,cntv=sumbit.sum(l-1,r),cntbit.sum(l-1,r) les.append([i,sumv-x*cntv]) else: a,i=q #このときはセグメント木をadd。iはユニークなのでaddでもupdateでもいい sumbit.add(i,a) cntbit.add(i,1) les.sort() for _,d in les: print(d) """ #[t5] """ Q = int(input()) query = [] for _ in range(Q): query.append(tuple(map(int, input().split()))) bit=BinaryIndexedTree([0]*200001) for t,x in query: if t==1: #なぜかupdateでも通ったけどaddにするのがただしいと思う bit.add(x,1) else: left=0 right=200001 while left+1<right: mid=left+(right-left)//2 if bit.sum(0,mid)>=x: #このときは左による right=mid else: left=mid #leftの削除も必要です。いまの場合削除とは0にすることにほかならないので bit.update(left,0) print(left) """ #[t6] 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) # 区間和のセグメント木と区間の要素数のセグメント木をどちらも用意します sumbit=BinaryIndexedTree([0]*N) numbit=BinaryIndexedTree([0]*N) les=[] for q in query: if len(q)==4: x,l,r,i=q #このとき区間の和を求める。和から要素数*xを引きます les.append([i,sumbit.sum(l-1,r)-x*numbit.sum(l-1,r)]) else: a,i=q #このときはセグメント木をupdate sumbit.update(i,a) numbit.update(i,1) les.sort() for _,d in les: print(d)