結果
問題 |
No.3101 Range Eratosthenes Query
|
ユーザー |
![]() |
提出日時 | 2025-05-24 15:38:52 |
言語 | PyPy3 (7.3.15) |
結果 |
AC
|
実行時間 | 2,471 ms / 3,000 ms |
コード長 | 8,912 bytes |
コンパイル時間 | 1,140 ms |
コンパイル使用メモリ | 82,712 KB |
実行使用メモリ | 342,560 KB |
最終ジャッジ日時 | 2025-05-24 15:39:53 |
合計ジャッジ時間 | 53,264 ms |
ジャッジサーバーID (参考情報) |
judge2 / judge1 |
(要ログイン)
ファイルパターン | 結果 |
---|---|
sample | AC * 2 |
other | AC * 24 |
ソースコード
#yukicoder3101 Range Eratosthenes Query #Wavelet Matrix import heapq class Wavelet_Matrix: #前提mod: 完備辞書(Pythonアレンジ版) class Fully_Indexable_Dictionary: def __init__(self, B: list[int], quick_select = False): self._N = N = len(B); self._x = x = 63; self._L = L = [0] * -(-N // x); self._C = C = [0] * -(-N // x); self._mask = [(1 << i) - 1 for i in range(1, x + 1)]; now = 0; self._count = [0] * 2; self._quick_select = (quick_select != False) if self._quick_select: self._Q = Q = [-1] + [N] * N for i in range(len(L)): L[i] = self._count[1] for k in range(x): if now >= N: break self._count[B[now]] += 1; C[i] += B[now] << k if quick_select: pos = self._count[0] if B[now] == 0 else -self._count[1]; Q[pos] = now now += 1 def __getitem__(self, i: int): return self._C[i // self._x] >> (i % self._x) & 1 def popcnt(self, N): N = (N & 0x5555555555555555) + ((N >> 1) & 0x5555555555555555); N = (N & 0x3333333333333333) + ((N >> 2) & 0x3333333333333333); N = (N & 0x0F0F0F0F0F0F0F0F) + ((N >> 4) & 0x0F0F0F0F0F0F0F0F); N = (N & 0x00FF00FF00FF00FF) + ((N >> 8) & 0x00FF00FF00FF00FF); N = (N & 0x0000FFFF0000FFFF) + ((N >> 16) & 0x0000FFFF0000FFFF); N = (N & 0x00000000FFFFFFFF) + ((N >> 32) & 0x00000000FFFFFFFF); return N def rank(self, i, num = 0): k, t = i // self._x, i % self._x; one = self._L[k] + self.popcnt( self._C[k] & self._mask[t] ); return 0 if i < 0 else one if num else 1 + i - one def select(self, k, num = 0): #rank(i,num) == kとなるi なければN O(logN) if k > self._count[num] or k == 0: return -1 if k == 0 else self._N if self._quick_select: return self._Q[k] if num == 0 else self._Q[-k] ok, ng = self._N, -1 while abs(ok - ng) > 1: mid = (ok + ng) // 2; ok, ng = (mid, ng) if self.rank(mid, num) >= k else (ok, mid) return ok def stable_sort(self, i, num = 0): return self.rank(i - 1, 0) if num == 0 else self._count[0] + self.rank(i - 1, 1) def range_sort(self, Lt, Rt, num = 0): z = self._count[0]; return (self.rank(Lt - 1, 0), self.rank(Rt - 1, 0)) if num == 0 else (z + self.rank(Lt - 1, 1), z + self.rank(Rt - 1, 1)) def __init__(self, A: list[int], quick_select = True): self._N = N = len(A); self._T, X, Y, Z = A[:], 1, [0] * N, [0] * N for num in A: X = max(num, X); assert num >= 0, 'non - negative int only.' self._logX = logX = X.bit_length(); self._B = B = [None] * logX; self._D = D = dict(); self._has_bit = has_bit = lambda S, x: S >> x & 1 for x in range(logX - 1, -1, -1): y = z = 0 for i in range(N): Y[i] = has_bit(self._T[i], x); z += 1 if Y[i] == 0 else 0 B[x] = self.Fully_Indexable_Dictionary(Y, quick_select) for i in range(N): if Y[i] == 0: Z[y] = self._T[i]; y += 1 elif Y[i] == 1: Z[z] = self._T[i]; z += 1 self._T, Z = Z, self._T for i in range(N): if self._T[i] not in D: D[self._T[i]] = i def __getitem__(self, i: int): #復元 O(logX) i %= self._N; c = 0 for k in range(self._logX - 1, -1, -1): b = self._B[k][i]; c += b << k; i = self._B[k].stable_sort(i, b) return c def rank(self, Lt, Rt, x): #半開区間[Lt:Rt)のxの出現回数 for k in range(self._logX - 1, -1, -1): c = self._has_bit(x, k); Lt, Rt = self._B[k].range_sort(Lt, Rt, c) return Rt - Lt def select(self, time, x): #time番目のxの位置 配列外は-1やNを返す if time == 0 or x not in self._D: return -1 p, c = self._D[x] + (time - 1), 0 if p >= self._N: return self._N for k in range(self._logX): b = self._has_bit(x, k); c += b << k if b == 1: p -= self._B[k]._count[0] p = self._B[k].select(p + 1, b) if p not in range(0, self._N): return self._N return p if x == c else self._N def get_min(self, Lt, Rt, time): #sorted( T[Lt:Rt) )の小さい方からt番目 if not 0 < time <= Rt - Lt: return -1 t = time - 1 for k in range(self._logX - 1, -1, -1): d = self._B[k].rank(Rt - 1, 0) - self._B[k].rank(Lt - 1, 0); b, t = (0, t) if t < d else (1, t - d); Lt, Rt = self._B[k].range_sort(Lt, Rt, b) return self._T[Lt + t] def range_freq(self, Lt, Rt, time): #頻出順にt個 O((Rt - Lt) logX) Q = [(~(Rt - Lt), ~(self._logX - 1), 0, Lt, Rt)]; ans = [] while Q and len(ans) < time: _, k, c, Lt, Rt = heapq.heappop(Q); k = ~k if not 0 <= Lt < Rt <= self._N: continue if k == -1: ans.append((c, Rt - Lt)); continue for d in range(2): a, b = self._B[k].range_sort(Lt, Rt, d) if b - a > 0: heapq.heappush(Q, (~(b - a), ~(k - 1), c + (d << k), a, b)) return ans def count(self, Lt, Rt, xL, xR): #T[Lt:Rt)中に出現する、xL <= T[i] < xR の個数 if not 0 <= Lt < Rt <= self._N or not 0 <= xL <= xR: return 0 ans = Rt - Lt if xL > 0: Q = [(0, self._logX - 1, Lt, Rt)] while Q: c, k, L, R = Q.pop() if c + (1 << (k + 1)) - 1 < xL: ans -= R - L; continue if k == -1: break for d in range(1, -1, -1): a, b = self._B[k].range_sort(L, R, d) if b - a > 0: Q.append((c + (d << k), k - 1, a, b)) if xR <= (1 << self._logX) - 1: Q = [(0, self._logX - 1, Lt, Rt)] while Q: c, k, L, R = Q.pop() if c >= xR: ans -= R - L; continue if k == -1: break for d in range(2): a, b = self._B[k].range_sort(L, R, d) if b - a > 0: Q.append((c + (d << k), k - 1, a, b)) return ans #T[Lt:Rt)中で、xL <= T[i] < xRを満たす 最小 / 最大値(条件を満たさなければ -1) def next_value(self, Lt, Rt, xL, xR, return_max = False): Q = [(0, self._logX - 1, Lt, Rt)]; R = range(2) if return_max else range(1, -1, -1) while Q: c, k, Lt, Rt = Q.pop() if not 0 <= Lt < Rt <= self._N or c + (1 << (k + 1)) - 1 < xL or c >= xR: continue if k == -1: return c for d in R: a, b = self._B[k].range_sort(Lt, Rt, d) if b - a > 0: Q.append((c + (d << k), k - 1, a, b)) else: return -1 def next_index(self, Lt, x): #T[Lt:)内にはじめて存在するxの添字 c = self.rank(0, Lt, x); return self.select(c + 1, x) def intersect(self, L1, R1, L2, R2): #T[L1:R1)とT[L2:R2)の共通要素を取り出す Q = [(0, self._logX - 1, L1, R1, L2, R2)]; ans = [] while Q: c, k, L1, R1, L2, R2 = Q.pop() if not 0 <= L1 < R1 <= self._N or not 0 <= L2 < R2 <= self._N: continue if k == -1: ans.append((c, min( R1 - L1, R2 - L2 ))); continue for d in range(1, -1, -1): a1, b1 = self._B[k].range_sort(L1, R1, d); a2, b2 = self._B[k].range_sort(L2, R2, d) if b1 - a1 > 0 and b2 - a2 > 0: Q.append((c + (d << k), k - 1, a1, b1, a2, b2)) return ans #愚直解 N = Ri - Li として O(NloglogN) def brute(Li, Ri): E = list(range(Li, Ri + 1)) ans = 0 for i in range(Li, Ri + 1): if E[i - Li] == i: ans += 1 for k in range(i, Ri + 1, i): E[k - Li] = i return ans #値nが食べられる条件は: [Li, Ri]内の最小約数がちょうどnであること #これの正当性を確認しておく def check(Li, Ri): E = [[] for _ in range(Ri + 1)] for n in range(1, Ri + 1): for k in range(n, Ri + 1, n): E[k].append(n) ans = 0 for n in range(Li, Ri + 1): assert E[n][-1] == n if len(E[n]) == 1 or E[n][-2] < Li: ans += 1 return ans #たぶん合ってそう なので最大約数表を作成 #最小素因数表は簡単に作れるのでそれを流用 E = list(range(10 ** 6 + 1)) for p in range(2, 10 ** 6 + 1): if E[p] == p: for k in range(p, 10 ** 6 + 1, p): E[k] = min(E[k], p) for n in range(1, 10 ** 6 + 1): E[n] = n // E[n] E[1] = 0 #便宜上 #静配列Eを与える。以下のクエリを処理せよ: #[Li, Ri]に含まれる、最小値がLi未満の値の個数を答えよ。 #クエリをLiでソートすれば解けるだろうが、ライブラリチェックしたい #のでWavelet Matrixで殴る WM = Wavelet_Matrix(E, True) for _ in range( Q := int(input()) ): Li, Ri = map(int, input().split()) print( WM.count(Li, Ri + 1, 0, Li) )