・再帰 import sys sys.setrecursionlimit(10**6) import pypyjit pypyjit.set_param('max_unroll_recursion=-1') デコレータ from types import GeneratorType def bootstrap(f, stack=[]): def wrappedfunc(*args, **kwargs): if stack: return f(*args, **kwargs) to = f(*args, **kwargs) while True: if type(to) is GeneratorType: stack.append(to) to = next(to) else: stack.pop() if not stack: break to = stack[-1].send(to) return to return wrappedfunc 〇使い方 1. @bootstrap を再帰関数の前につける 2. returnはyieldと書く 3. 自身を呼び出す場合は先頭にyieldと書く(計算式の中などで呼び出す場合は括弧で囲む [(yield func(a))]) 4. 戻り値がない場合でも、最後にyieldと書く 5. 再帰上限の変更は書く必要がない ・実行速度の計測 import time start = time.time() end = time.time() print(end - start) ・UnionFind class UnionFind: def __init__(self,n): self.n = n self.parent_size = [-1]*n def leader(self,a): if self.parent_size[a] < 0: return a self.parent_size[a] = self.leader(self.parent_size[a]) return self.parent_size[a] def merge(self,a,b): x, y = self.leader(a), self.leader(b) if x == y: return if abs(self.parent_size[x]) < abs(self.parent_size[y]): x, y = y, x self.parent_size[x] += self.parent_size[y] self.parent_size[y] = x return def same(self,a,b): return self.leader(a) == self.leader(b) def size(self,a): return abs(self.parent_size[self.leader(a)]) def groups(self): result = [[] for _ in range(self.n)] for i in range(self.n): result[self.leader(i)].append(i) return [r for r in result if r != []] ・素数判定 def isPrime(n): if n <= 1: return False else: i = 2 while i**2 <= n: if n%i == 0: return False i += 1 return True ・エラトステネスのふるい def eratosthenes(n): sieve = [True] * (n + 1) for i in range(int(n**0.5) + 1): if i < 2: sieve[i] = False elif sieve[i]: for j in range(i, n//i + 1): sieve[i * j] = False ans = [] for i in range(n+1): if sieve[i]: ans.append(i) return ans ・10進数→n進数 def base_n(num_10,n): str_n = '' while num_10: if num_10%n>=10: return -1 str_n += str(num_10%n) num_10 //= n return int(str_n[::-1]) ・ユークリッドの互除法 def gcd(a, b): r = a % b while r != 0: a, b = b, r r = a % b return b ・素因数分解 from math import isqrt def factorization(n): arr = [] temp = n for i in range(2, isqrt(n)+1): if temp%i == 0: cnt = 0 while temp%i == 0: cnt += 1 temp //= i arr.append([i, cnt]) if temp != 1: arr.append([temp, 1]) if arr == []: arr.append([n, 1]) return arr ・約数列挙 def divisor(n): ans = [] for i in range(1, int(n**0.5)+1): if n % i == 0: ans.append(i) if i*i != n: ans.append(n//i) return ans ・2点間の距離 def distance(x1, y1, x2, y2): return ((x2-x1)**2 + (y2-y1)**2) ** 0.5 (比較演算をする場合はルートを外してかわりに反対側を2乗する) ・トリミング def trimming(A): minH = len(A) maxH = 0 minW = len(A[0]) maxW = 0 for i in range(len(A)): for j in range(len(A[0])): if A[i][j] == "#": minH = min(i, minH) maxH = max(i, maxH) minW = min(j, minW) maxW = max(j, maxW) a = [] for i in range(minH, maxH+1): a.append(A[i][minW:maxW+1]) return a ・回転(時計回り) def rotateR(A): return list(map(list, zip(*A[::-1]))) ・回転(反時計回り) def rotateL(A): return list(map(list, zip(*A)))[::-1] ・ランダムアクセスがO(1)のdeque class Deque: def __init__(self, src_arr=[], max_size=300000): self.N = max(max_size, len(src_arr)) + 1 self.buf = list(src_arr) + [None] * (self.N - len(src_arr)) self.head = 0 self.tail = len(src_arr) def __index(self, i): l = len(self) if not -l <= i < l: raise IndexError('index out of range: ' + str(i)) if i < 0: i += l return (self.head + i) % self.N def __extend(self): ex = self.N - 1 self.buf[self.tail+1 : self.tail+1] = [None] * ex self.N = len(self.buf) if self.head > 0: self.head += ex def is_full(self): return len(self) >= self.N - 1 def is_empty(self): return len(self) == 0 def append(self, x): if self.is_full(): self.__extend() self.buf[self.tail] = x self.tail += 1 self.tail %= self.N def appendleft(self, x): if self.is_full(): self.__extend() self.buf[(self.head - 1) % self.N] = x self.head -= 1 self.head %= self.N def pop(self): if self.is_empty(): raise IndexError('pop() when buffer is empty') ret = self.buf[(self.tail - 1) % self.N] self.tail -= 1 self.tail %= self.N return ret def popleft(self): if self.is_empty(): raise IndexError('popleft() when buffer is empty') ret = self.buf[self.head] self.head += 1 self.head %= self.N return ret def __len__(self): return (self.tail - self.head) % self.N def __getitem__(self, key): return self.buf[self.__index(key)] def __setitem__(self, key, value): self.buf[self.__index(key)] = value def __str__(self): return 'Deque({0})'.format(str(list(self))) ・セグメント木 def op(x, y): return class SegTree: def __init__(self, init_val, op, ide_ele): n = len(init_val) self.n = n self.op = op self.ide_ele = ide_ele self.num = 1 << (n - 1).bit_length() self.tree = [ide_ele] * 2 * self.num for i in range(n): self.tree[self.num + i] = init_val[i] for i in range(self.num - 1, 0, -1): self.tree[i] = self.op(self.tree[2 * i], self.tree[2 * i + 1]) def update(self, k, x): k += self.num self.tree[k] = x while k > 1: l, r = k, k^1 if l > r: l, r = r, l self.tree[k >> 1] = self.op(self.tree[l], self.tree[r]) k >>= 1 def query(self, l, r): res_l = self.ide_ele res_r = self.ide_ele l += self.num r += self.num while l < r: if l & 1: res_l = self.op(res_l, self.tree[l]) l += 1 if r & 1: res_r = self.op(self.tree[r - 1], res_r) l >>= 1 r >>= 1 return self.op(res_l, res_r) def __getitem__(self, n): return self.tree[self.num+n] def List(self): return self.tree[self.num:self.num+self.n] def max_right(self, l, f, limit): if l == self.n: return self.n l += self.num sm = self.ide_ele while True: while l%2 == 0: l >>= 1 if not f(self.op(sm, self.tree[l]), limit): while l < self.num: l <<= 1 if f(self.op(sm, self.tree[l]), limit): sm = self.op(sm, self.tree[l]) l += 1 return l-self.num sm = self.op(sm, self.tree[l]) l += 1 if l & -l == l: break return self.n def min_left(self, r, f, limit): if r == 0: return 0 r += self.num sm = self.ide_ele while True: r -= 1 while r > 1 and r%2 == 1: r >>= 1 if not f(self.op(self.tree[r], sm), limit): while r < self.num: r = 2*r+1 if f(self.op(self.tree[r], sm), limit): sm = self.op(self.tree[r], sm) r -= 1 return r+1-self.num sm = self.op(self.tree[r], sm) if r & -r == r: break return 0 〇使い方 1. 初期化用のリストを用意 2. 区間に行う操作をsegfuncに書き込む ・最小値 min(x, y) ・最大値 max(x, y) ・区間和 x + y ・区間積 x * y ・最大公約数 math.gcd(x, y) 3. 生成 seg = SegTree(リスト, segfunc, (単位元)) ・単位元 ・最小値 float('inf') ・最大値 -float('inf') ・区間和 0 ・区間積 1 ・最大公約数 0 4. 操作 0-index ・更新 seg.update(k, x) k番目の要素をxに更新 ・区間の操作の結果を取得 seg.query(l, r) l以上r未満の区間から値を取得 ・ダイクストラ法 def dijkstra(start): INF = 10**18 dist = [INF]*N dist[start] = 0 visited = [False]*N que = [(0, start)] while que: d, now = heappop(que) if visited[now]: continue visited[now] = True for next, weight in G[now]: if dist[now]+weight < dist[next]: dist[next] = dist[now]+weight heappush(que, (dist[next], next)) return dist ・Ford-Fulkerson class maxflow_edge: def __init__(self, to, cap, rev): self.to = to self.cap = cap self.rev = rev @bootstrap def dfs(pos, goal, F, G, visited): if pos == goal: yield F visited[pos] = True for e in G[pos]: if not visited[e.to] and e.cap > 0: flow = yield dfs(e.to, goal, min(F, e.cap), G, visited) if flow >= 1: e.cap -= flow G[e.to][e.rev].cap += flow yield flow yield 0 def maxflow(s, t, N): G = [[] for i in range(N)] for a, b, c in edge: G[a].append(maxflow_edge(b, c, len(G[b]))) G[b].append(maxflow_edge(a, 0, len(G[a])-1)) total_flow = 0 while True: visited = [False]*N F = dfs(s, t, INF, G, visited) if F > 0: total_flow += F else: break return total_flow ・逆元 def inverse(n, d): return n * pow(d, -1, MOD) % MOD ・遅延セグメント木 〇区間加算用 def segfunc(x,y): return class LazySegTree_RAQ: def __init__(self,init_val,segfunc,ide_ele): n = len(init_val) self.segfunc = segfunc self.ide_ele = ide_ele self.num = 1<<(n-1).bit_length() self.tree = [ide_ele]*2*self.num self.lazy = [0]*2*self.num for i in range(n): self.tree[self.num+i] = init_val[i] for i in range(self.num-1,0,-1): self.tree[i] = self.segfunc(self.tree[2*i], self.tree[2*i+1]) def gindex(self,l,r): l += self.num r += self.num lm = l>>(l&-l).bit_length() rm = r>>(r&-r).bit_length() ans = [] while r>l: if l<=lm: ans.append(l) if r<=rm: ans.append(r) r >>= 1 l >>= 1 while l: ans.append(l) l >>= 1 return ans def propagates(self,ids): for i in reversed(ids): v = self.lazy[i] if v==0: continue self.lazy[i] = 0 self.lazy[2*i] += v self.lazy[2*i+1] += v self.tree[2*i] += v self.tree[2*i+1] += v def add(self,l,r,x): ids = self.gindex(l,r) l += self.num r += self.num while l>= 1 l >>= 1 for i in ids: self.tree[i] = self.segfunc(self.tree[2*i], self.tree[2*i+1]) + self.lazy[i] def query(self,l,r): self.propagates(self.gindex(l,r)) res = self.ide_ele l += self.num r += self.num while l>= 1 r >>= 1 return res 〇区間更新用 def segfunc(x,y): return class LazySegTree_RUQ: def __init__(self,init_val,segfunc,ide_ele): n = len(init_val) self.segfunc = segfunc self.ide_ele = ide_ele self.num = 1<<(n-1).bit_length() self.tree = [ide_ele]*2*self.num self.lazy = [None]*2*self.num for i in range(n): self.tree[self.num+i] = init_val[i] for i in range(self.num-1,0,-1): self.tree[i] = self.segfunc(self.tree[2*i],self.tree[2*i+1]) def gindex(self,l,r): l += self.num r += self.num lm = l>>(l&-l).bit_length() rm = r>>(r&-r).bit_length() ans = [] while r>l: if l<=lm: ans.append(l) if r<=rm: ans.append(r) r >>= 1 l >>= 1 while l: ans.append(l) l >>= 1 return ans def propagates(self,ids): for i in reversed(ids): v = self.lazy[i] if v is None: continue self.lazy[i] = None self.lazy[2*i] = v self.lazy[2*i+1] = v self.tree[2*i] = v self.tree[2*i+1] = v def update(self,l,r,x): ids = self.gindex(l,r) self.propagates(ids) l += self.num r += self.num while l>= 1 l >>= 1 for i in ids: self.tree[i] = self.segfunc(self.tree[2*i], self.tree[2*i+1]) def query(self,l,r): self.propagates(self.gindex(l,r)) res = self.ide_ele l += self.num r += self.num while l>= 1 r >>= 1 return res 〇使い方 1. 初期化用のリストを用意 2. 区間に行う操作をsegfuncに書き込む ・最小値 min(x, y) ・最大値 max(x, y) ・区間和 x + y ・区間積 x * y ・最大公約数 math.gcd(x, y) 3. 生成 seg = SegTree(リスト, segfunc, (単位元)) ・単位元 ・最小値 float('inf') ・最大値 -float('inf') ・区間和 0 ・区間積 1 ・最大公約数 0 4. 操作 0-index ・更新 seg.add(l, r, x) l以上r未満の区間にxを加算 seg.update(l, r, x) l以上r未満の区間をxに更新 ・区間の操作の結果を取得 seg.query(l, r) l以上r未満の区間から値を取得 ・素数リスト 18625498411672028939 93638244515503437017 15508888921625689043 桁最大の素数 1桁⋅⋅⋅7 2桁⋅⋅⋅97 3桁⋅⋅⋅997 4桁⋅⋅⋅9973 5桁⋅⋅⋅99991 6桁⋅⋅⋅999983 7桁⋅⋅⋅9999991 8桁⋅⋅⋅99999989 9桁⋅⋅⋅999999937 10桁⋅⋅⋅9999999967 11桁⋅⋅⋅99999999977 12桁⋅⋅⋅999999999989 13桁⋅⋅⋅9999999999971 14桁⋅⋅⋅99999999999973 15桁⋅⋅⋅999999999999989 16桁⋅⋅⋅9999999999999937 17桁⋅⋅⋅99999999999999997 18桁⋅⋅⋅999999999999999989 19桁⋅⋅⋅9999999999999999961 20桁⋅⋅⋅99999999999999999989 桁最小の素数 1桁⋅⋅⋅2 2桁⋅⋅⋅11 3桁⋅⋅⋅101 4桁⋅⋅⋅1009 5桁⋅⋅⋅10007 6桁⋅⋅⋅100003 7桁⋅⋅⋅1000003 8桁⋅⋅⋅10000019 9桁⋅⋅⋅100000007 10桁⋅⋅⋅1000000007 11桁⋅⋅⋅10000000019 12桁⋅⋅⋅100000000003 13桁⋅⋅⋅1000000000039 14桁⋅⋅⋅10000000000037 15桁⋅⋅⋅100000000000031 16桁⋅⋅⋅1000000000000037 17桁⋅⋅⋅10000000000000061 18桁⋅⋅⋅100000000000000003 19桁⋅⋅⋅1000000000000000003 20桁⋅⋅⋅10000000000000000051 ・強連結成分分解 def scc(N,edges): M=len(edges) start=[0]*(N+1) elist=[0]*M for e in edges: start[e[0]+1]+=1 for i in range(1,N+1): start[i]+=start[i-1] counter=start[:] for e in edges: elist[counter[e[0]]]=e[1] counter[e[0]]+=1 visited=[] low=[0]*N Ord=[-1]*N ids=[0]*N NG=[0,0] def dfs(v): stack=[(v,-1,0),(v,-1,1)] while stack: v,bef,t=stack.pop() if t: if bef!=-1 and Ord[v]!=-1: low[bef]=min(low[bef],Ord[v]) stack.pop() continue low[v]=NG[0] Ord[v]=NG[0] NG[0]+=1 visited.append(v) for i in range(start[v],start[v+1]): to=elist[i] if Ord[to]==-1: stack.append((to,v,0)) stack.append((to,v,1)) else: low[v]=min(low[v],Ord[to]) else: if low[v]==Ord[v]: while(True): u=visited.pop() Ord[u]=N ids[u]=NG[1] if u==v: break NG[1]+=1 low[bef]=min(low[bef],low[v]) for i in range(N): if Ord[i]==-1: dfs(i) for i in range(N): ids[i]=NG[1]-1-ids[i] group_num=NG[1] counts=[0]*group_num for x in ids: counts[x]+=1 groups=[[] for i in range(group_num)] for i in range(N): groups[ids[i]].append(i) return groups ・拡張ユークリッドの互除法 def extended_euclid(a, b): if b == 0: return (1, 0) else: xd, yd = extended_euclid(b, a % b) return (yd, xd - a // b * yd) ・重み付きUnionFind class WeightedUnionFind: def __init__(self, n): self.par = [i for i in range(n)] self.rank = [0] * n self.weight = [0] * n def leader(self, x): if self.par[x] == x: return x else: y = self.leader(self.par[x]) self.weight[x] += self.weight[self.par[x]] self.par[x] = y return y def merge(self, x, y, w): rx = self.leader(x) ry = self.leader(y) if self.rank[rx] < self.rank[ry]: self.par[rx] = ry self.weight[rx] = w - self.weight[x] + self.weight[y] else: self.par[ry] = rx self.weight[ry] = -w - self.weight[y] + self.weight[x] if self.rank[rx] == self.rank[ry]: self.rank[rx] += 1 def same(self, x, y): return self.leader(x) == self.leader(y) def diff(self, x, y): return self.weight[x] - self.weight[y] ・3点が同一線上にあるか def judge(x1, y1, x2, y2, x3, y3): return (x1-x3)*(y2-y3)-(x2-x3)*(y1-y3) == 0 ・全方位木dp def LRset(n): for i in range(1, len(G[n])): L[n][i] = L[n][i-1]+dp[n][i-1] for i in reversed(range(len(G[n])-1)): R[n][i] = R[n][i+1]+dp[n][i+1] L = [[0]*len(G[i]) for i in range(N)] R = [[0]*len(G[i]) for i in range(N)] LRset(0) @bootstrap def rerooting(n, pre, no): visited[n] = True preno = 0 for i in range(len(G[n])): if G[n][i] == pre: preno = i break dp[n][preno] = L[pre][no]+R[pre][no]+1 LRset(n) for i in range(len(G[n])): if not visited[G[n][i]]: yield rerooting(G[n][i], n, i) yield visited = [False]*N visited[0] = True for i in range(len(G[0])): if not visited[G[0][i]]: rerooting(G[0][i], 0, i) ・Z-algorithm def z_algo(S): N = len(S) A = [0]*N i = 1; j = 0 A[0] = l = len(S) while i < l: while i+j < l and S[j] == S[i+j]: j += 1 if not j: i += 1 continue A[i] = j k = 1 while l-i > k < j - A[k]: A[i+k] = A[k] k += 1 i += k; j -= k return A ・ローリングハッシュ class RollingHash: def __init__(self, S, type): if type == False: a = ord("a")-1 else: a = ord("A")-1 self.hash = [0] for i in range(len(S)): self.hash.append((self.hash[-1]*26+(ord(S[i])-a))%MOD) self.POW = [1] for i in range(len(S)): self.POW.append(self.POW[-1]*26%MOD) def query(self, l, r): return (self.hash[r]-self.hash[l]*self.POW[r-l])%MOD ・最小共通祖先 class LCA: def __init__(self, G, root=0): V = len(G) self.bit_length = 1 while 1< self.depth[r]: sml = self.segfunc(sml, self.Tree.query(li, ri)) else: smr = self.segfunc(self.Tree.query(ri, li), smr) return self.segfunc(sml, smr) else: l_top, r_top = self.top[l], self.top[r] if self.depth[l_top] > self.depth[r_top]: sml = self.segfunc(sml, self.Tree.query(li, self.node[l_top]+1)) l = self.parent[l_top] else: smr = self.segfunc(self.Tree.query(ri, self.node[r_top]+1), smr) r = self.parent[r_top] @bootstrap def build1(self, n): self.visited[n] = True for v, _ in self.G[n]: if not self.visited[v]: self.parent[v] = n self.depth[v] = self.depth[n]+1 self.size[n] += yield self.build1(v) if self.heavy[n] == -1 or self.size[self.heavy[n]] < self.size[v]: self.heavy[n] = v yield self.size[n] @bootstrap def build2(self, n): self.visited[n] = True if self.heavy[n] != -1: v = self.heavy[n] self.top[v] = self.top[n] self.HLD_G.append(v) yield self.build2(v) for v, _ in self.G[n]: if not self.visited[v] and v != self.heavy[n]: self.top[v] = v self.HLD_G.append(v) yield self.build2(v) yield def make_tree(self): for i, n in enumerate(self.HLD_G[:-1]): for v, w in self.G[n]: if v == self.parent[n]: self.node_w[i] = w break self.node[n] = i self.Tree = SegTree(self.node_w, self.segfunc, self.ide_ele) ・HL分解(頂点) class HLDecomposition: def __init__(self, N, G, segfunc, ide_ele): self.N = N self.G = G self.segfunc = segfunc self.ide_ele = ide_ele self.parent = [-1]*N self.depth = [0]*N self.size = [1]*N self.heavy = [-1]*N self.visited = [False]*N self.build1(0) self.top = [0]*N self.HLD_G = [0] self.visited = [False]*N self.build2(0) self.HLD_G = self.HLD_G[::-1] self.node_w = [0]*N self.node = [N-1]*N self.make_tree() def update(self, n, weight): self.Tree.update(self.node[n], weight) def query(self, l, r): sml, smr = self.ide_ele, self.ide_ele while True: li, ri = self.node[l], self.node[r] if self.top[l] == self.top[r]: if self.depth[l] > self.depth[r]: sml = self.segfunc(sml, self.Tree.query(li, ri+1)) else: smr = self.segfunc(self.Tree.query(ri, li+1), smr) return self.segfunc(sml, smr) else: l_top, r_top = self.top[l], self.top[r] if self.depth[l_top] > self.depth[r_top]: sml = self.segfunc(sml, self.Tree.query(li, self.node[l_top]+1)) l = self.parent[l_top] else: smr = self.segfunc(self.Tree.query(ri, self.node[r_top]+1), smr) r = self.parent[r_top] @bootstrap def build1(self, n): self.visited[n] = True for v in self.G[n]: if not self.visited[v]: self.parent[v] = n self.depth[v] = self.depth[n]+1 self.size[n] += yield self.build1(v) if self.heavy[n] == -1 or self.size[self.heavy[n]] < self.size[v]: self.heavy[n] = v yield self.size[n] @bootstrap def build2(self, n): self.visited[n] = True if self.heavy[n] != -1: v = self.heavy[n] self.top[v] = self.top[n] self.HLD_G.append(v) yield self.build2(v) for v in self.G[n]: if not self.visited[v] and v != self.heavy[n]: self.top[v] = v self.HLD_G.append(v) yield self.build2(v) yield def make_tree(self): for i, n in enumerate(self.HLD_G): self.node_w[i] = A[n] self.node[n] = i self.Tree = SegTree(self.node_w, self.segfunc, self.ide_ele) ・等比数列 def geometric_progression(a, r, n): # a = 初項 r = 公比 n = 項数 return a*((pow(r, n, MOD)-1)%MOD)%MOD*inverse(1, r-1)%MOD ・ベクトル def vec(a, b): return (b[0]-a[0], b[1]-a[1]) ・ベクトルの絶対値 def vec2(a): return (a[0]**2+a[1]**2)**0.5 ・内積 def inner_product(v1, v2): return v1[0]*v2[0]+v1[1]*v2[1] ・外積 def cross_product(v1, v2): return v1[0]*v2[1]-v1[1]*v2[0] ・∠BACの角度 def angle(bx, by, ax, ay, cx, cy): ab = vec((ax, ay), (bx, by)) ac = vec((ax, ay), (cx, cy)) top = ab[0]*ac[0]+ab[1]*ac[1] if cross_product(ab, ac) <= 0: return degrees(acos(top/(vec2(ab)*vec2(ac)))) else: return 360-degrees(acos(top/(vec2(ab)*vec2(ac)))) ・組み合わせの数 class CP: def __init__(self, N): self.fact = [1]*(N+1) self.fact_inv = [1]*(N+1) for i in range(2, N+1): self.fact[i] = self.fact[i-1]*i%MOD self.fact_inv[N] = pow(self.fact[N], -1, MOD) for i in reversed(range(1, N)): self.fact_inv[i] = self.fact_inv[i+1]*(i+1)%MOD def C(self, N, K): if N < 0 or K < 0 or N < K: return 0 return self.fact[N]*self.fact_inv[K]%MOD*self.fact_inv[N-K]%MOD def P(self, N, K): if N < 0 or K < 0 or N < K: return 0 return self.fact[N]*self.fact_inv[N-K]%MOD def H(self, N, K): if N < 0 or K < 0: return 0 if N == K == 0: return 1 return self.C(N+K-1, K) ・Dinic class Dinic_edge: def __init__(self, t, cap, rev): self.t = t self.cap = cap self.rev = rev class Dinic: def __init__(self, N): self.N = N self.G = [[] for _ in range(N)] def add_edge(self, u, v, cap): self.G[u].append(Dinic_edge(v, cap, len(self.G[v]))) self.G[v].append(Dinic_edge(u, 0, len(self.G[u])-1)) def bfs(self, s, to): self.level = [-1]*self.N que = deque() que.append(s) self.level[s] = 0 while que: n = que.popleft() for v in self.G[n]: if v.cap and self.level[v.t] == -1: self.level[v.t] = self.level[n]+1 que.append(v.t) return self.level[to] != -1 @bootstrap def dfs(self, n, to, f): if n == to: yield f for v in self.it[n]: if v.cap and self.level[n] < self.level[v.t]: d = yield self.dfs(v.t, to, min(f, v.cap)) if d: v.cap -= d self.G[v.t][v.rev].cap += d yield d yield 0 def flow(self, s, t): flow = 0 INF = 10**18 while self.bfs(s, t): *self.it, = map(iter, self.G) f = INF while f: f = self.dfs(s, t, INF) flow += f return flow ・ポラード・ロー素因数分解 def gcd(a, b): while a: a, b = b%a, a return b def is_prime(n): if n == 2: return 1 if n == 1 or n%2 == 0: return 0 m = n - 1 lsb = m & -m s = lsb.bit_length()-1 d = m // lsb test_numbers = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37] for a in test_numbers: if a == n: continue x = pow(a,d,n) r = 0 if x == 1: continue while x != m: x = pow(x,2,n) r += 1 if x == 1 or r == s: return 0 return 1 def find_prime_factor(n): if n%2 == 0: return 2 m = int(n**0.125)+1 for c in range(1,n): f = lambda a: (pow(a,2,n)+c)%n y = 0 g = q = r = 1 k = 0 while g == 1: x = y while k < 3*r//4: y = f(y) k += 1 while k < r and g == 1: ys = y for _ in range(min(m, r-k)): y = f(y) q = q*abs(x-y)%n g = gcd(q,n) k += m k = r r *= 2 if g == n: g = 1 y = ys while g == 1: y = f(y) g = gcd(abs(x-y),n) if g == n: continue if is_prime(g): return g elif is_prime(n//g): return n//g else: return find_prime_factor(g) def factorize(n): res = [] while not is_prime(n) and n > 1: # nが合成数である間nの素因数の探索を繰り返す p = find_prime_factor(n) s = 0 while n%p == 0: # nが素因数pで割れる間割り続け、出力に追加 n //= p s += 1 res.append((p, s)) if n > 1: # n>1であればnは素数なので出力に追加 res.append((n, 1)) return res def divisor(n): ans = [1] pf = factorize(n) for p, c in pf: L = len(ans) for i in range(L): v = 1 for _ in range(c): v *= p ans.append(ans[i]*v) return sorted(ans) ・行列の積 def matrix(a, b): ans = [[0]*len(b[0]) for _ in range(len(a))] for i in range(len(a)): for j in range(len(b[0])): for k in range(len(b)): ans[i][j] += a[i][k]*b[k][j]%MOD ans[i][j] %= MOD return ans ・座標と行列から移動後の座標を求める def place(a, b): X = sum(a[0][i]*b[i] for i in range(3)) Y = sum(a[1][i]*b[i] for i in range(3)) return X, Y ・変換行列 def OP(n, p): # 時計回りに90度回転 if n == 1: A = [(0, 1, 0), (-1, 0, 0), (0, 0, 1)] # 反時計回りに90度回転 elif n == 2: A = [(0, -1, 0), (1, 0, 0), (0, 0, 1)] # x軸pに対称な位置に移動 elif n == 3: A = [(-1, 0, 2*p), (0, 1, 0), (0, 0, 1)] # y軸pに対称な位置に移動 else: A = [(1, 0, 0), (0, -1, 2*p), (0, 0, 1)] return A ・WaveletMatrix from itertools import accumulate from typing import Optional class BitVector: def __init__(self, B: list[int]): self.B = B self.acc = list(accumulate(B)) def __len__(self) -> int: return len(self.B) def __getitem__(self, i: int) -> int: return self.B[i] def rank0(self, i: int) -> int: if i <= 0: return 0 i = min(i, len(self)) return i - self.rank1(i) def rank1(self, i: int) -> int: if i <= 0: return 0 i = min(i, len(self)) return self.acc[i - 1] def rank0_all(self) -> int: return self.rank0(len(self.B)) def rank1_all(self) -> int: return self.rank1(len(self.B)) def select0(self, k: int) -> Optional[int]: if (k <= 0) or (k > self.rank0_all()): return None left, right = 0, len(self) while (right - left) > 1: mid = (left + right) // 2 if self.rank0(mid) < k: left = mid else: right = mid return left def select1(self, k: int) -> Optional[int]: if (k <= 0) or (k > self.rank1_all()): return None left, right = 0, len(self) while (right - left) > 1: mid = (left + right) // 2 if self.rank1(mid) < k: left = mid else: right = mid return left class WaveletMatrix: def __init__(self, T: list[int]): self.bit_size: int = max(T).bit_length() if T else 0 self.wavelet_matrix: list[BitVector] = self._build(T) def _build(self, T: list[int]) -> list[BitVector]: wavelet_matrix: list[BitVector] = [] for digit in range(self.bit_size)[::-1]: zeros = [t for t in T if not self._get_i_bit(t, digit + 1)] ones = [t for t in T if self._get_i_bit(t, digit + 1)] T = zeros + ones wavelet_matrix.append(BitVector([self._get_i_bit(t, digit) for t in T])) return wavelet_matrix def __len__(self) -> int: if len(self.wavelet_matrix) == 0: return 0 return len(self.wavelet_matrix[0]) def __getitem__(self, i: int) -> int: return self.access(i) def _get_i_bit(self, x: int, digit: int) -> int: return 1 & (x >> digit) def _next_range(self, B: BitVector, bit: int, left: int, right: int) -> tuple[int, int]: if bit == 0: left = B.rank0(left) right = B.rank0(right) else: left = B.rank0_all() + B.rank1(left) right = B.rank0_all() + B.rank1(right) return (left, right) def access(self, i: int) -> int: x = 0 for digit, B in enumerate(self.wavelet_matrix): bit = B[i] x |= bit << (self.bit_size - digit - 1) if bit == 0: i = B.rank0(i + 1) - 1 else: i = B.rank0_all() + B.rank1(i + 1) - 1 return x # T[0..right)におけるxの出現回数 def rank(self, x: int, right: int) -> int: return self.rank_range(x, 0, right) # T[left..right)におけるxの出現回数 def rank_range(self, x: int, left: int, right: int) -> int: if (left >= right) or (x.bit_length() > self.bit_size) or (x < 0): return 0 left = max(left, 0) right = min(right, len(self)) for digit, B in enumerate(self.wavelet_matrix): x_bit = self._get_i_bit(x, self.bit_size - digit - 1) left, right = self._next_range(B, x_bit, left, right) return right - left # 元の配列Tのk個目のxの出現位置 (index) def select(self, x: int, k: int) -> Optional[int]: if not (0 <= x < 1 << self.bit_size): return None if k <= 0: return None # xが最終的な配列のどの範囲に入るか計算 left, right = 0, len(self) for digit, B in enumerate(self.wavelet_matrix): x_bit = self._get_i_bit(x, self.bit_size - digit - 1) left, right = self._next_range(B, x_bit, left, right) if left >= right: return None # 上記範囲のk番目が, 元の配列で何番目か計算 index = left + k - 1 for digit, B in enumerate(self.wavelet_matrix[::-1]): if index is None: return None x_bit = self._get_i_bit(x, digit) if x_bit == 0: index = B.select0(index + 1) else: index = B.select1(index - (B.rank0_all() - 1)) return index # 元の配列T[left..right)の中のk番目に小さい値 def quantile(self, left: int, right: int, k: int) -> Optional[int]: if (left >= right) or (k <= 0) or (right - left < k): return None left = max(left, 0) right = min(right, len(self)) x = 0 for digit, B in enumerate(self.wavelet_matrix): num_zeros = B.rank0(right) - B.rank0(left) # 該当要素が0の中か or 1の中か bit = 0 if k <= num_zeros else 1 left, right = self._next_range(B, bit, left, right) if bit == 1: k -= num_zeros x |= bit << (self.bit_size - digit - 1) return x # T[left..right)の0 <= x < upper となるxの個数 def range_freq_to(self, left: int, right: int, upper: int) -> int: if (left >= right) or (upper <= 0): return 0 # 全ての要素が < upper if upper.bit_length() > self.bit_size: return right - left cnt = 0 left = max(left, 0) right = min(right, len(self)) for digit, B in enumerate(self.wavelet_matrix): upper_bit = self._get_i_bit(upper, self.bit_size - digit - 1) if upper_bit == 1: cnt += B.rank0(right) - B.rank0(left) left, right = self._next_range(B, upper_bit, left, right) return cnt # T[left..right)のlower <= x となるxの個数 def range_freq_from(self, left: int, right: int, lower: int) -> int: if (left >= right) or (lower > (1 << self.bit_size)): return 0 left = max(left, 0) right = min(right, len(self)) return (right - left) - self.range_freq_to(left, right, lower) # T[left..right)のlower <= x < upper となるxの個数 def range_freq(self, left: int, right: int, lower: int, upper: int) -> int: if (left >= right) or (lower >= upper) or (lower > (1 << self.bit_size)): return 0 return self.range_freq_to(left, right, upper) - self.range_freq_to( left, right, lower ) # T[left..right)の中で0 <= x < upperを満たす最大のx def prev_value(self, left: int, right: int, upper: int) -> Optional[int]: cnt = self.range_freq_to(left, right, upper) if cnt == 0: return None return self.quantile(left, right, cnt) # T[left..right)の中で, lower <= x を満たす最小のx def next_value(self, left: int, right: int, lower: int) -> int: cnt = self.range_freq_from(left, right, lower) if cnt == 0: return None return self.quantile(left, right, right - left - cnt + 1) ・HopcroftKarp import sys sys.setrecursionlimit(10**6) class HopcroftKarp: def __init__(self, N1, N2): self.N1 = N1 self.N2 = N2 self.G = [[] for _ in range(self.N1+1)] self.pair1 = [0]*(self.N1+1) self.pair2 = [0]*(self.N2+1) self.matching_size = -1 def add_edge(self, fr, to): self.G[fr].append(to) def bfs(self): que = deque() for i in range(1, self.N1+1): if self.pair1[i] == 0: self.dist[i] = 0 que.append(i) else: self.dist[i] = INF self.dist[0] = INF while que: n = que.popleft() if self.dist[n] < self.dist[0]: for v in self.G[n]: if self.dist[self.pair2[v]] == INF: self.dist[self.pair2[v]] = self.dist[n]+1 que.append(self.pair2[v]) return self.dist[0] != INF def dfs(self, n): if n != 0: for v in self.G[n]: if self.dist[self.pair2[v]] == self.dist[n]+1: if self.dfs(self.pair2[v]): self.pair2[v] = n self.pair1[n] = v return True self.dist[n] = INF return False return True def flow(self): if self.matching_size != -1: return self.matching_size self.dist = [0]*(self.N1+1) ans = 0 while self.bfs(): for i in range(1, self.N1+1): if self.pair1[i] == 0 and self.dfs(i): ans += 1 self.matching_size = ans return ans def get_matching(self): if self.matching_size == -1: self.flow() ans = [] for i in range(1, self.N1+1): if self.pair1[i] != 0: ans.append((i, self.pair1[i])) return ans def minimum_vertex_cover(self): if self.matching_size == -1: self.flow() return self.matching_size def maximum_independent_set(self): return self.N1+self.N2-self.minimum_vertex_cover() def minimum_edge_cover(self): F = [False]*(self.N1+self.N2+1) for n in range(1, self.N1+1): F[n] = True for v in self.G[n]: F[self.N1+v] = True if sum(F) < self.N1+self.N2: return -1 if self.matching_size == -1: self.flow() return self.N1+self.N2-self.matching_size def preparation(self): if self.matching_size == -1: self.flow() L = self.N1+self.N2 G = [[] for _ in range(L+1)] for n in range(1, self.N1+1): for v in self.G[n]: if self.pair1[n] == v: G[self.N1+v].append(n) else: G[n].append(self.N1+v) visited = [False]*(L+1) que = deque() for i in range(1, self.N1+1): if self.pair1[i] == 0: visited[i] = True que.append(i) while que: n = que.popleft() for v in G[n]: if not visited[v]: visited[v] = True que.append(v) return visited def get_minimum_vertex_cover(self): visited = self.preparation() ans1 = [] ans2 = [] for i in range(1, self.N1+self.N2+1): if i <= self.N1 and not visited[i]: ans1.append(i) elif self.N1+1 <= i and visited[i]: ans2.append(i-self.N1) return ans1, ans2 def get_maximum_independent_set(self): visited = self.preparation() ans1 = [] ans2 = [] for i in range(1, self.N1+self.N2+1): if i <= self.N1 and visited[i]: ans1.append(i) elif self.N1+1 <= i and not visited[i]: ans2.append(i-self.N1) return ans1, ans2 def get_minimum_edge_cover(self): cnt = self.minimum_edge_cover() if cnt == -1: return None ans = [] pair = [-1]*(self.N1+self.N2+1) for n in range(1, self.N1+1): pair[n] = self.G[n][0] for v in self.G[n]: if pair[self.N1+v] == -1: pair[self.N1+v] = n for i in range(1, self.N1+self.N2+1): if i <= self.N1 and self.pair1[i] != 0: ans.append((i, self.pair1[i])) elif i <= self.N1 and self.pair1[i] == 0: ans.append((i, pair[i])) elif self.N1+1 <= i and self.pair2[i-self.N1] == 0: ans.append((pair[i], i-self.N1)) return ans ・Suffix Array(SA-IS) from collections import Counter # SA-IS (O(nlogn)) # s: 文字列 def sais(s): uniq = list(set(s)) uniq.sort() return sais_rec(list(map({e: i+1 for i, e in enumerate(uniq)}.__getitem__, s)), len(uniq)) def sais_rec(lst, num): L = len(lst) if L < 2: return lst + [0] lst = lst + [0] L += 1 res = [-1] * L t = [1] * L for i in range(L-2, -1, -1): t[i] = 1 if (lst[i] < lst[i+1] or (lst[i] == lst[i+1] and t[i+1])) else 0 isLMS = [t[i-1] < t[i] for i in range(L)] LMS = [i for i in range(1, L) if t[i-1] < t[i]] LMSn = len(LMS) count = Counter(lst) tmp = 0 cstart = [0]*(num+1) cend = [0]*(num+1) for key in range(num+1): cstart[key] = tmp cend[key] = tmp = tmp + count[key] cc_it = [iter(range(e-1, s-1, -1)) for s, e in zip(cstart, cend)] for e in reversed(LMS): res[next(cc_it[lst[e]])] = e cs_it = [iter(range(s, e)) for s, e in zip(cstart, cend)] ce_it = [iter(range(e-1, s-1, -1)) for s, e in zip(cstart, cend)] for e in res: if e > 0 and not t[e-1]: res[next(cs_it[lst[e-1]])] = e-1 for e in reversed(res): if e > 0 and t[e-1]: res[next(ce_it[lst[e-1]])] = e-1 name = 0; prev = -1 pLMS = {} for e in res: if isLMS[e]: if prev == -1 or lst[e] != lst[prev]: name += 1; prev = e else: for i in range(1, L): if lst[e+i] != lst[prev+i]: name += 1; prev = e break if isLMS[e+i] or isLMS[prev+i]: break pLMS[e] = name-1 if name < LMSn: sublst = [pLMS[e] for e in LMS if e < L-1] ret = sais_rec(sublst, name-1) LMS = list(map(LMS.__getitem__, reversed(ret))) else: LMS = [e for e in reversed(res) if isLMS[e]] res = [-1] * L cc_it = [iter(range(e-1, s-1, -1)) for s, e in zip(cstart, cend)] for e in LMS: res[next(cc_it[lst[e]])] = e cs_it = [iter(range(s, e)) for s, e in zip(cstart, cend)] ce_it = [iter(range(e-1, s-1, -1)) for s, e in zip(cstart, cend)] for e in res: if e > 0 and not t[e-1]: res[next(cs_it[lst[e-1]])] = e-1 for e in reversed(res): if e > 0 and t[e-1]: res[next(ce_it[lst[e-1]])] = e-1 return res # Longest Common Prefix # (文字列s, 文字列長n, Suffix Array)を引数として与える def LCP(s, n, sa): lcp = [-1]*(n+1) rank = [0]*(n+1) for i in range(n+1): rank[sa[i]] = i h = 0 lcp[0] = 0 for i in range(n): j = sa[rank[i] - 1] if h > 0: h -= 1 while j+h < n and i+h < n and s[j+h]==s[i+h]: h += 1 lcp[rank[i] - 1] = h return lcp ・最小費用流 # https://atcoder.jp/contests/practice2/submissions/33588016 from heapq import heappush, heappop class MinCostFlow: """ https://github.com/atcoder/ac-library/blob/master/atcoder/internal_csr.hpp https://github.com/atcoder/ac-library/blob/master/atcoder/mincostflow.hpp https://github.com/atcoder/ac-library/blob/master/document_en/mincostflow.md https://github.com/atcoder/ac-library/blob/master/document_ja/mincostflow.md """ def __init__(self, n): self.n = n self._edges = [] def add_edge(self, fr, to, cap, cost): assert 0 <= fr < self.n assert 0 <= to < self.n assert 0 <= cap assert 0 <= cost self._edges.append(self.edge(fr, to, cap, cost)) return len(self._edges) - 1 class edge: def __init__(self, fr, to, cap, cost): self.fr = fr self.to = to self.cap = cap self.flow = 0 self.cost = cost def get_edge(self, i): assert 0 <= i < len(self._edges) return self._edges[i] def edges(self): return self._edges def flow(self, s, t, flow_limit=1<<60): return self.slope(s, t, flow_limit)[-1] def __csr(self, edges): # Compressed Sparse Row self.start = [0] * (self.n + 1) for fr, _ in edges: self.start[fr + 1] += 1 for i in range(self.n): self.start[i + 1] += self.start[i] counter = self.start.copy() self.elist = [0] * len(edges) for fr, e in edges: self.elist[counter[fr]] = e counter[fr] += 1 class _edge: def __init__(self, to, rev, cap, cost): self.to = to self.rev = rev self.cap = cap self.cost = cost def __g(self): degree = [0] * self.n redge_idx = [0] * self.m elist = [(0, None)] * (2 * self.m) now_elist = 0 for i in range(self.m): e = self._edges[i] self.edge_idx[i] = degree[e.fr] degree[e.fr] += 1 redge_idx[i] = degree[e.to] degree[e.to] += 1 elist[now_elist] = (e.fr, self._edge(e.to, -1, e.cap - e.flow, e.cost)) now_elist += 1 elist[now_elist] = (e.to, self._edge(e.fr, -1, e.flow, -e.cost)) now_elist += 1 self.__csr(elist) for i in range(self.m): e = self._edges[i] self.edge_idx[i] += self.start[e.fr] redge_idx[i] += self.start[e.to] self.elist[self.edge_idx[i]].rev = redge_idx[i] self.elist[redge_idx[i]].rev = self.edge_idx[i] def slope(self, s, t, flow_limit=1<<60): assert 0 <= s < self.n assert 0 <= t < self.n assert s != t self.m = len(self._edges) self.edge_idx = [0] * self.m self.__g() result = self.__slope(s, t, flow_limit) for i in range(self.m): e = self.elist[self.edge_idx[i]] self._edges[i].flow = self._edges[i].cap - e.cap return result def __dual_ref(self, s, t): log = self.n.bit_length() mask = (1<= 0 (all reduced cost are positive) # dist[v] <= (n-1)C dual_v = self.dual[v] dist_v = dist[v] for i in range(self.start[v], self.start[v+1]): e = self.elist[i] if not e.cap: continue # |-dual[e.to] + dual[v]| <= (n-1)C # cost <= C - -(n-1)C + 0 = nC cost = e.cost - self.dual[e.to] + dual_v if dist[e.to] - dist_v > cost: dist_to = dist_v + cost dist[e.to] = dist_to self.prev_e[e.to] = e.rev if dist_to == dist_v: que_min.append(e.to) else: heappush(que, dist_to<= 0 - (n-1)C self.dual[v] -= dist[t] - dist[v] return True def __slope(self, s, t, flow_limit): # variants (C = maxcost): # -(n-1)C <= dual[s] <= dual[i] <= dual[t] = 0 # reduced cost (= e.cost + dual[e.from] - dual[e.to]) >= 0 for all edge self.dual = [0] * self.n self.prev_e = [0] * self.n flow = 0 cost = 0 prev_cost_per_flow = -1 result = [(0, 0)] while flow < flow_limit: if not self.__dual_ref(s, t): break c = flow_limit - flow v = t while v != s: c = min(c, self.elist[self.elist[self.prev_e[v]].rev].cap) v = self.elist[self.prev_e[v]].to v = t while v != s: e = self.elist[self.prev_e[v]] e.cap += c self.elist[e.rev].cap -= c v = self.elist[self.prev_e[v]].to d = -self.dual[s] flow += c cost += c * d if prev_cost_per_flow == d: result.pop() result.append((flow, cost)) prev_cost_per_flow = d return result ・エラトステネスのふるいを用いた高速素因数分解と高速約数列挙 class Eratosthenes: def __init__(self, n): self.isPrime = [True]*(n+1) self.minfactor = [-1]*(n+1) self.isPrime[0], self.isPrime[1] = False, False self.minfactor[1] = 1 for i in range(2, n+1): if self.isPrime[i]: self.minfactor[i] = i for j in range(i*i, n+1, i): self.isPrime[j] = False if self.minfactor[j] == -1: self.minfactor[j] = i def factorize(self, n): factor = [] while n > 1: p = self.minfactor[n] cnt = 0 while self.minfactor[n] == p: n //= p cnt += 1 factor.append((p, cnt)) return factor def divisor(self, n): ans = [1] pf = self.factorize(n) for p, c in pf: L = len(ans) for i in range(L): v = 1 for _ in range(c): v *= p ans.append(ans[i]*v) return ans ・高速ゼータ変換、高速メビウス変換 def Eratosthenes(n): isPrime = [True]*(n+1) isPrime[0], isPrime[1] = False, False for i in range(2, n+1): if isPrime[i]: for j in range(i*i, n+1, i): isPrime[j] = False return isPrime def fast_zeta(F): isPrime = Eratosthenes(len(F)) print(F) for i in range(2, len(F)): if isPrime[i]: for j in range((len(F)-1)//i, 0, -1): F[j] += F[j*i] print(F) def fast_mobius(F): isPrime = Eratosthenes(len(F)) for i in range(2, len(F)): if isPrime[i]: for j in range(1, (len(F)-1)//i+1): F[j] -= F[j*i] ・メビウス関数値の列挙 class Eratosthenes: def __init__(self, n): self.isPrime = [True]*(n+1) self.isPrime[0], self.isPrime[1] = False, False self.mobius = [1]*(n+1) for i in range(2, n+1): if self.isPrime[i]: self.mobius[i] = -1 for j in range(i*2, n+1, i): self.isPrime[j] = False if j//i%i == 0: self.mobius[j] = 0 else: self.mobius[j] *= -1 ・オイラーのφ関数 def Euler(n): div = divisor(n) ans = 0 E = Eratosthenes(n) for d in div: ans += n//d*E.mobius[d] return ans ・座標圧縮 def compress(A): S = sorted(set(A)) D = dict() for i in range(len(S)): D[S[i]] = i return D ・マージソートツリー(X以下や以上の個数) class MergeSortTree: def __init__(self, init_val): n = len(init_val) self.n = n self.num = 1<<(n-1).bit_length() self.tree = [[] for _ in range(self.num*2)] for i in range(n): self.tree[self.num+i].append(init_val[i]) for i in range(self.num-1, 0, -1): left, right = 2*i, 2*i+1 idxL, idxR = 0, 0 while idxL < len(self.tree[left]) or idxR < len(self.tree[right]): if idxL == len(self.tree[left]): self.tree[i].append(self.tree[right][idxR]) idxR += 1 elif idxR == len(self.tree[right]): self.tree[i].append(self.tree[left][idxL]) idxL += 1 else: if self.tree[left][idxL] <= self.tree[right][idxR]: self.tree[i].append(self.tree[left][idxL]) idxL += 1 else: self.tree[i].append(self.tree[right][idxR]) idxR += 1 # bisectのleftとrightを書き換えれば、Xより小さい数 or Xより大きい数 のクエリにも対応可能 # sum(A[i] <= X for i in range(l, r)) def query1(self, l, r, X): ans = 0 l += self.num r += self.num while l < r: if l & 1: ans += bisect_right(self.tree[l], X) l += 1 if r & 1: ans += bisect_right(self.tree[r-1], X) l >>= 1 r >>= 1 return ans # sum(A[i] >= X for i in range(l, r)) def query2(self, l, r, X): ans = 0 l += self.num r += self.num while l < r: if l & 1: ans += len(self.tree[l])-bisect_left(self.tree[l], X) l += 1 if r & 1: ans += len(self.tree[r-1])-bisect_left(self.tree[r-1], X) l >>= 1 r >>= 1 return ans # sum(leftX <= A[i] <= rightX for i in range(l, r)) def query3(self, l, r, leftX, rightX): ans = r-l l += self.num r += self.num while l < r: if l & 1: ans -= bisect_left(self.tree[l], leftX) ans -= len(self.tree[l])-bisect_right(self.tree[l], rightX) l += 1 if r & 1: ans -= bisect_left(self.tree[r-1], leftX) ans -= len(self.tree[r-1])-bisect_right(self.tree[r-1], rightX) l >>= 1 r >>= 1 return ans ・マージソートツリー(X以下や以上の個数、総和) class MergeSortTree: def __init__(self, init_val): n = len(init_val) self.n = n self.num = 1<<(n-1).bit_length() self.tree = [[] for _ in range(self.num*2)] self.cum = [[0] for _ in range(self.num*2)] for i in range(n): self.tree[self.num+i].append(init_val[i]) for i in range(self.num-1, 0, -1): left, right = 2*i, 2*i+1 idxL, idxR = 0, 0 while idxL < len(self.tree[left]) or idxR < len(self.tree[right]): if idxL == len(self.tree[left]): self.tree[i].append(self.tree[right][idxR]) idxR += 1 elif idxR == len(self.tree[right]): self.tree[i].append(self.tree[left][idxL]) idxL += 1 else: if self.tree[left][idxL] <= self.tree[right][idxR]: self.tree[i].append(self.tree[left][idxL]) idxL += 1 else: self.tree[i].append(self.tree[right][idxR]) idxR += 1 for i in range(1, self.num*2): for j in range(len(self.tree[i])): self.cum[i].append(self.cum[i][-1]+self.tree[i][j]) # bisectのleftとrightを書き換えれば、Xより小さい数 or Xより大きい数 のクエリにも対応可能 # sum(A[i] <= X for i in range(l, r)) def query1(self, l, r, X): ans = 0 l += self.num r += self.num while l < r: if l & 1: ans += bisect_right(self.tree[l], X) l += 1 if r & 1: ans += bisect_right(self.tree[r-1], X) l >>= 1 r >>= 1 return ans # sum(A[i] >= X for i in range(l, r)) def query2(self, l, r, X): ans = 0 l += self.num r += self.num while l < r: if l & 1: ans += len(self.tree[l])-bisect_left(self.tree[l], X) l += 1 if r & 1: ans += len(self.tree[r-1])-bisect_left(self.tree[r-1], X) l >>= 1 r >>= 1 return ans # sum(leftX <= A[i] <= rightX for i in range(l, r)) def query3(self, l, r, leftX, rightX): ans = r-l l += self.num r += self.num while l < r: if l & 1: ans -= bisect_left(self.tree[l], leftX) ans -= len(self.tree[l])-bisect_right(self.tree[l], rightX) l += 1 if r & 1: ans -= bisect_left(self.tree[r-1], leftX) ans -= len(self.tree[r-1])-bisect_right(self.tree[r-1], rightX) l >>= 1 r >>= 1 return ans # sum(A[i] for i in range(l, r) if A[i] <= X) def query4(self, l, r, X): ans = 0 l += self.num r += self.num while l < r: if l & 1: b = bisect_right(self.tree[l], X) ans += self.cum[l][b] l += 1 if r & 1: b = bisect_right(self.tree[r-1], X) ans += self.cum[r-1][b] l >>= 1 r >>= 1 return ans # sum(A[i] for i in range(l, r) if A[i] >= X) def query5(self, l, r, X): ans = 0 l += self.num r += self.num while l < r: if l & 1: b = bisect_left(self.tree[l], X) ans += self.cum[l][-1]-self.cum[l][b] l += 1 if r & 1: b = bisect_left(self.tree[r-1], X) ans += self.cum[r-1][-1]-self.cum[r-1][b] l >>= 1 r >>= 1 return ans # sum(A[i] for i in range(l, r) if leftX <= A[i] <= rightX) def query6(self, l, r, leftX, rightX): ans = 0 l += self.num r += self.num while l < r: if l & 1: left = bisect_left(self.tree[l], leftX) right = bisect_right(self.tree[l], rightX) ans += self.cum[l][right]-self.cum[l][left] l += 1 if r & 1: left = bisect_left(self.tree[r-1], leftX) right = bisect_right(self.tree[r-1], rightX) ans += self.cum[r-1][right]-self.cum[r-1][left] l >>= 1 r >>= 1 return ans ・行列の積 def matrix(a, b): ans = [[0]*len(b[0]) for _ in range(len(a))] for i in range(len(a)): for j in range(len(b[0])): ans[i][j] = sum(a[i][k]*b[k][j] for k in range(len(b))) return ans ・中国剰余定理 def CRT(B, M): r, m = 0, 1 for i in range(len(B)): d, x, y = extended_euclid(m, M[i]) if (B[i]-r)%d != 0: return 0, -1 tmp = (B[i]-r)//d*x%(M[i]//d) r += m*tmp m *= M[i]//d return r, m ・BIT class BIT: def __init__(self, A): self.size = len(A) self.bit = [0]*(len(A)+1) for i in range(len(A)): self.add(i, A[i]) def sum(self, i): i += 1 ans = 0 while i > 0: ans += self.bit[i] i -= -i&i return ans def query(self, l, r): if l == 0: return self.sum(r-1) else: return self.sum(r-1)-self.sum(l-1) def add(self, i, x): i += 1 while i <= self.size: self.bit[i] += x i += -i&i ・線分の交差判定 def judge(ax, ay, bx, by, cx, cy, dx, dy): s = (ax-bx)*(cy-ay)-(ay-by)*(cx-ax) t = (ax-bx)*(dy-ay)-(ay-by)*(dx-ax) if s*t > 0: return False s = (cx-dx)*(ay-cy)-(cy-dy)*(ax-cx) t = (cx-dx)*(by-cy)-(cy-dy)*(bx-cx) if s*t > 0: return False return True ・二次元累積和 class cum2: def __init__(self, A): self.cum = [[0]*(len(A[0])+1) for _ in range(len(A)+1)] for i in range(len(A)): for j in range(len(A[0])): self.cum[i+1][j+1] = A[i][j] for i in range(1, len(A)+1): for j in range(2, len(A[0])+1): self.cum[i][j] += self.cum[i][j-1] for i in range(2, len(A)+1): for j in range(1, len(A[0])+1): self.cum[i][j] += self.cum[i-1][j] def query(self, lh, rh, lw, rw): return self.cum[rh+1][rw+1]+self.cum[lh][lw]-self.cum[rh+1][lw]-self.cum[lh][rw+1] ・三次元累積和 class cum3: def __init__(self, A): self.cum = [[[0]*(len(A[0][0])+1) for _ in range(len(A[0])+1)] for _ in range(len(A)+1)] for i in range(len(A)): for j in range(len(A[0])): for k in range(len(A[0][0])): self.cum[i+1][j+1][k+1] = A[i][j][k] for i in range(1, len(A)+1): for j in range(1, len(A[0])+1): for k in range(2, len(A[0][0])+1): self.cum[i][j][k] += self.cum[i][j][k-1] for i in range(1, len(A)+1): for j in range(2, len(A[0])+1): for k in range(1, len(A[0][0])+1): self.cum[i][j][k] += self.cum[i][j-1][k] for i in range(2, len(A)+1): for j in range(1, len(A[0])+1): for k in range(1, len(A[0][0])+1): self.cum[i][j][k] += self.cum[i-1][j][k] def query(self, lh, rh, ld, rd, lw, rw): return (self.cum[rh+1][rd+1][rw+1]+self.cum[rh+1][ld][lw]-self.cum[rh+1][rd+1][lw]-self.cum[rh+1][ld][rw+1] -self.cum[lh][rd+1][rw+1]-self.cum[lh][ld][lw]+self.cum[lh][rd+1][lw]+self.cum[lh][ld][rw+1]) ・LowLink class LowLink: def __init__(self, N): self.N = N self.M = 0 self.G = [[] for _ in range(N)] def add_edge(self, u, v): self.G[u].append((v, self.M)) self.G[v].append((u, self.M)) self.M += 1 @bootstrap def dfs(self, n, parent): self.visited[n] = True self.ord[n], self.low[n] = self.cnt, self.cnt self.cnt += 1 is_ap = False child = 0 for v, idx in self.G[n]: if not self.visited[v]: child += 1 yield self.dfs(v, idx) self.low[n] = min(self.low[n], self.low[v]) if parent != -1 and self.ord[n] <= self.low[v]: is_ap = True if self.ord[n] < self.low[v]: self.bridge.append(idx) elif idx != parent: self.low[n] = min(self.low[n], self.ord[v]) if parent == -1 and 2 <= child: is_ap = True if is_ap: self.articulation_points.append(n) yield def find(self): self.ord = [-1]*self.N self.low = [-1]*self.N self.visited = [False]*self.N self.cnt = 0 self.articulation_points = [] self.bridge = [] for i in range(self.N): if not self.visited[i]: self.dfs(i, -1) return sorted(self.articulation_points), sorted(self.bridge) ・Trie木 class Trie: def __init__(self, N, type): self.tree = [[-1]*26 for _ in range(N+1)] self.arrive = [0]*(N+1) self.last = [0]*(N+1) self.nex = 1 if type == 0: self.base = ord("a") else: self.base = ord("A") def insert(self, S): now = 0 self.arrive[now] += 1 for s in S: if self.tree[now][ord(s)-self.base] == -1: self.tree[now][ord(s)-self.base] = self.nex now = self.nex self.nex += 1 else: now = self.tree[now][ord(s)-self.base] self.arrive[now] += 1 self.last[now] += 1 def search(self, S): now = 0 for s in S: if self.tree[now][ord(s)-self.base] == -1: return 0 now = self.tree[now][ord(s)-self.base] return self.last[now] def start_with(self, S): now = 0 for s in S: if self.tree[now][ord(s)-self.base] == -1: return 0 now = self.tree[now][ord(s)-self.base] return self.arrive[now] def count(self): return self.arrive[0] ・分割 # dp[i][j] jをi以下の数字で分割する個数 def division1(n): dp = [[0]*(n+1) for _ in range(n+1)] dp[0][0] = 1 for i in range(1, n+1): for j in range(n+1): if j-i >= 0: dp[i][j] = (dp[i-1][j]+dp[i][j-i])%MOD else: dp[i][j] = dp[i-1][j] return dp # dp[i][j] iを長さがjの数字に分割する個数 def division2(n): dp = [[0]*(n+1) for _ in range(n+1)] dp[0][0] = 1 for i in range(1, n+1): for j in range(1, n+1): if i-j >= 0: dp[i][j] = (dp[i-1][j-1]+dp[i-j][j])%MOD else: dp[i][j] = dp[i-1][j-1] return dp # dp[i][j] iを長さがj以下の数字に分割する個数 def division3(n): dp = [[0]*(n+1) for _ in range(n+1)] dp[0][0] = 1 for i in range(1, n+1): for j in range(1, n+1): if i-j >= 0: dp[i][j] = (dp[i][j-1]+dp[i-j][j])%MOD else: dp[i][j] = dp[i][j-1] return dp ・SortedSet(tatyam) # https://github.com/tatyam-prime/SortedSet/blob/main/SortedSet.py import math from bisect import bisect_left, bisect_right from typing import Generic, Iterable, Iterator, List, Tuple, TypeVar, Optional T = TypeVar('T') class SortedSet(Generic[T]): BUCKET_RATIO = 16 SPLIT_RATIO = 24 def __init__(self, a: Iterable[T] = []) -> None: "Make a new SortedSet from iterable. / O(N) if sorted and unique / O(N log N)" a = list(a) n = len(a) if any(a[i] > a[i + 1] for i in range(n - 1)): a.sort() if any(a[i] >= a[i + 1] for i in range(n - 1)): a, b = [], a for x in b: if not a or a[-1] != x: a.append(x) n = self.size = len(a) num_bucket = int(math.ceil(math.sqrt(n / self.BUCKET_RATIO))) self.a = [a[n * i // num_bucket : n * (i + 1) // num_bucket] for i in range(num_bucket)] def __iter__(self) -> Iterator[T]: for i in self.a: for j in i: yield j def __reversed__(self) -> Iterator[T]: for i in reversed(self.a): for j in reversed(i): yield j def __eq__(self, other) -> bool: return list(self) == list(other) def __len__(self) -> int: return self.size def __repr__(self) -> str: return "SortedSet" + str(self.a) def __str__(self) -> str: s = str(list(self)) return "{" + s[1 : len(s) - 1] + "}" def _position(self, x: T) -> Tuple[List[T], int, int]: "return the bucket, index of the bucket and position in which x should be. self must not be empty." for i, a in enumerate(self.a): if x <= a[-1]: break return (a, i, bisect_left(a, x)) def __contains__(self, x: T) -> bool: if self.size == 0: return False a, _, i = self._position(x) return i != len(a) and a[i] == x def add(self, x: T) -> bool: "Add an element and return True if added. / O(√N)" if self.size == 0: self.a = [[x]] self.size = 1 return True a, b, i = self._position(x) if i != len(a) and a[i] == x: return False a.insert(i, x) self.size += 1 if len(a) > len(self.a) * self.SPLIT_RATIO: mid = len(a) >> 1 self.a[b:b+1] = [a[:mid], a[mid:]] return True def _pop(self, a: List[T], b: int, i: int) -> T: ans = a.pop(i) self.size -= 1 if not a: del self.a[b] return ans def discard(self, x: T) -> bool: "Remove an element and return True if removed. / O(√N)" if self.size == 0: return False a, b, i = self._position(x) if i == len(a) or a[i] != x: return False self._pop(a, b, i) return True def lt(self, x: T) -> Optional[T]: "Find the largest element < x, or None if it doesn't exist." for a in reversed(self.a): if a[0] < x: return a[bisect_left(a, x) - 1] def le(self, x: T) -> Optional[T]: "Find the largest element <= x, or None if it doesn't exist." for a in reversed(self.a): if a[0] <= x: return a[bisect_right(a, x) - 1] def gt(self, x: T) -> Optional[T]: "Find the smallest element > x, or None if it doesn't exist." for a in self.a: if a[-1] > x: return a[bisect_right(a, x)] def ge(self, x: T) -> Optional[T]: "Find the smallest element >= x, or None if it doesn't exist." for a in self.a: if a[-1] >= x: return a[bisect_left(a, x)] def __getitem__(self, i: int) -> T: "Return the i-th element." if i < 0: for a in reversed(self.a): i += len(a) if i >= 0: return a[i] else: for a in self.a: if i < len(a): return a[i] i -= len(a) raise IndexError def pop(self, i: int = -1) -> T: "Pop and return the i-th element." if i < 0: for b, a in enumerate(reversed(self.a)): i += len(a) if i >= 0: return self._pop(a, ~b, i) else: for b, a in enumerate(self.a): if i < len(a): return self._pop(a, b, i) i -= len(a) raise IndexError def index(self, x: T) -> int: "Count the number of elements < x." ans = 0 for a in self.a: if a[-1] >= x: return ans + bisect_left(a, x) ans += len(a) return ans def index_right(self, x: T) -> int: "Count the number of elements <= x." ans = 0 for a in self.a: if a[-1] > x: return ans + bisect_right(a, x) ans += len(a) return ans ・SortedMultiSet(tatyam) # https://github.com/tatyam-prime/SortedSet/blob/main/SortedMultiset.py import math from bisect import bisect_left, bisect_right from typing import Generic, Iterable, Iterator, List, Tuple, TypeVar, Optional T = TypeVar('T') class SortedMultiset(Generic[T]): BUCKET_RATIO = 16 SPLIT_RATIO = 24 def __init__(self, a: Iterable[T] = []) -> None: "Make a new SortedMultiset from iterable. / O(N) if sorted / O(N log N)" a = list(a) n = self.size = len(a) if any(a[i] > a[i + 1] for i in range(n - 1)): a.sort() num_bucket = int(math.ceil(math.sqrt(n / self.BUCKET_RATIO))) self.a = [a[n * i // num_bucket : n * (i + 1) // num_bucket] for i in range(num_bucket)] def __iter__(self) -> Iterator[T]: for i in self.a: for j in i: yield j def __reversed__(self) -> Iterator[T]: for i in reversed(self.a): for j in reversed(i): yield j def __eq__(self, other) -> bool: return list(self) == list(other) def __len__(self) -> int: return self.size def __repr__(self) -> str: return "SortedMultiset" + str(self.a) def __str__(self) -> str: s = str(list(self)) return "{" + s[1 : len(s) - 1] + "}" def _position(self, x: T) -> Tuple[List[T], int, int]: "return the bucket, index of the bucket and position in which x should be. self must not be empty." for i, a in enumerate(self.a): if x <= a[-1]: break return (a, i, bisect_left(a, x)) def __contains__(self, x: T) -> bool: if self.size == 0: return False a, _, i = self._position(x) return i != len(a) and a[i] == x def count(self, x: T) -> int: "Count the number of x." return self.index_right(x) - self.index(x) def add(self, x: T) -> None: "Add an element. / O(√N)" if self.size == 0: self.a = [[x]] self.size = 1 return a, b, i = self._position(x) a.insert(i, x) self.size += 1 if len(a) > len(self.a) * self.SPLIT_RATIO: mid = len(a) >> 1 self.a[b:b+1] = [a[:mid], a[mid:]] def _pop(self, a: List[T], b: int, i: int) -> T: ans = a.pop(i) self.size -= 1 if not a: del self.a[b] return ans def discard(self, x: T) -> bool: "Remove an element and return True if removed. / O(√N)" if self.size == 0: return False a, b, i = self._position(x) if i == len(a) or a[i] != x: return False self._pop(a, b, i) return True def lt(self, x: T) -> Optional[T]: "Find the largest element < x, or None if it doesn't exist." for a in reversed(self.a): if a[0] < x: return a[bisect_left(a, x) - 1] def le(self, x: T) -> Optional[T]: "Find the largest element <= x, or None if it doesn't exist." for a in reversed(self.a): if a[0] <= x: return a[bisect_right(a, x) - 1] def gt(self, x: T) -> Optional[T]: "Find the smallest element > x, or None if it doesn't exist." for a in self.a: if a[-1] > x: return a[bisect_right(a, x)] def ge(self, x: T) -> Optional[T]: "Find the smallest element >= x, or None if it doesn't exist." for a in self.a: if a[-1] >= x: return a[bisect_left(a, x)] def __getitem__(self, i: int) -> T: "Return the i-th element." if i < 0: for a in reversed(self.a): i += len(a) if i >= 0: return a[i] else: for a in self.a: if i < len(a): return a[i] i -= len(a) raise IndexError def pop(self, i: int = -1) -> T: "Pop and return the i-th element." if i < 0: for b, a in enumerate(reversed(self.a)): i += len(a) if i >= 0: return self._pop(a, ~b, i) else: for b, a in enumerate(self.a): if i < len(a): return self._pop(a, b, i) i -= len(a) raise IndexError def index(self, x: T) -> int: "Count the number of elements < x." ans = 0 for a in self.a: if a[-1] >= x: return ans + bisect_left(a, x) ans += len(a) return ans def index_right(self, x: T) -> int: "Count the number of elements <= x." ans = 0 for a in self.a: if a[-1] > x: return ans + bisect_right(a, x) ans += len(a) return ans ・区間更新 区間和取得 class Node: def __init__(self, value, size): self.value = value self.size = size def op(left, right): return Node(left.value+right.value, left.size+right.size) def mapping(top, bottom): if top != INF: return Node(top*bottom.size, bottom.size) else: return bottom def composition(top, bottom): if top != INF: return top else: return bottom e = Node(0, 0) ID = INF ・木の重心 @bootstrap def dfs(n, pre): cnt = 1 MAX = -(10**18) for i, v in enumerate(G[n]): if v != pre: c = yield dfs(v, n) cnt += c if MAX < c: MAX = c maxidx[n] = i size[n] = cnt yield size[n] size = [-1]*N maxidx = [-1]*N dfs(0, -1) @bootstrap def dfs2(n): if size[G[n][maxidx[n]]] > N//2: yield (yield dfs2(G[n][maxidx[n]])) yield n center = dfs2(0) ・3点を結ぶ三角形の面積 def area(x1, y1, x2, y2, x3, y3): return abs((x1-x3)*(y2-y3)-(x2-x3)*(y1-y3))/2 ・ChatGPT用 The following code is what I wrote to solve a problem in an AtCoder contest. When using generative AI to translate programming languages during an ongoing AtCoder contest, there are the following restrictions: "It is absolutely necessary to include the original code at the beginning of the submission as a comment or similar." "Only translations that do not alter the algorithm are permitted. In particular, any changes that would affect the time complexity are strictly prohibited." For any parts that cannot be directly translated, please mark them as "FAILED" and leave them unconverted. Following these strict AtCoder rules, please translate the following code from Python to C++. ・文字と数字の変換 def code(s): return ord(s)-ord("a") def codeR(n): return chr(ord("a")+n) ・組み合わせの数(k == 2 or k == 3) def nC2(n): return n*(n-1)//2 def nC3(n): return n*(n-1)*(n-2)//6 ・無限等比級数の和 def infinite(a, r): # a = 初項 r = 公比 return inverse(a, 1-r) ・UnionFind(値を保持) class UnionFind: def __init__(self, n, A): self.n = n self.parent_size = [-1]*n self.A = A[:] def leader(self, a): if self.parent_size[a] < 0: return a self.parent_size[a] = self.leader(self.parent_size[a]) return self.parent_size[a] def merge(self, a, b): x, y = self.leader(a), self.leader(b) if x == y: return a = self.A[x] if abs(self.parent_size[x]) < abs(self.parent_size[y]): x, y = y, x self.parent_size[x] += self.parent_size[y] self.parent_size[y] = x self.A[x] = a return def __getitem__(self, n): return self.A[self.leader(n)] def update(self, n, a): self.A[self.leader(n)] = a def same(self, a, b): return self.leader(a) == self.leader(b) def size(self, a): return abs(self.parent_size[self.leader(a)]) def groups(self): result = [[] for _ in range(self.n)] for i in range(self.n): result[self.leader(i)].append(i) return [r for r in result if r != []] ・UnionFind(値を演算) def op(x, y): return class UnionFind: def __init__(self, n, A, op): self.n = n self.parent_size = [-1]*n self.A = A[:] self.op = op def leader(self, a): if self.parent_size[a] < 0: return a self.parent_size[a] = self.leader(self.parent_size[a]) return self.parent_size[a] def merge(self, a, b): x, y = self.leader(a), self.leader(b) if x == y: return l, r = self.A[x], self.A[y] if abs(self.parent_size[x]) < abs(self.parent_size[y]): x, y = y, x self.parent_size[x] += self.parent_size[y] self.parent_size[y] = x self.A[x] = self.op(l, r) return def __getitem__(self, n): return self.A[self.leader(n)] def update(self, n, a): self.A[self.leader(n)] = a def same(self, a, b): return self.leader(a) == self.leader(b) def size(self, a): return abs(self.parent_size[self.leader(a)]) def groups(self): result = [[] for _ in range(self.n)] for i in range(self.n): result[self.leader(i)].append(i) return [r for r in result if r != []] ・ランレングス圧縮 def RLE(A): ans = [] cnt = 0 for i in range(len(A)): cnt += 1 if i+1 == len(A) or A[i] != A[i+1]: ans.append((A[i], cnt)) cnt = 0 return ans def RLER(A): ans = [] for s, c in A: ans.append(s*c) return "".join(ans) ・Manacher def Manacher(S): C = [""] for s in S: C.append(s) C.append("") R = [0]*len(C) i = 0 j = 0 while i < len(C): while 0 <= i-j and i+j < len(C) and C[i-j] == C[i+j]: j += 1 R[i] = j k = 1 while 0 <= i-k and k+R[i-k] < j: R[i+k] = R[i-k] k += 1 i += k j -= k odd = [] even = [] for i in range(1, len(C)-1): if i%2 == 1: odd.append(R[i]//2) else: even.append(R[i]//2) return odd, even ・凸包 def cross3(a, b, c): return (b[0]-a[0])*(c[1]-a[1]) - (b[1]-a[1])*(c[0]-a[0]) def convex_hull(ps): ps.sort() qs = [] L = len(ps) for p in ps: # 一直線上で高々2点にする場合は ">=" にする while len(qs) > 1 and cross3(qs[-1], qs[-2], p) > 0: qs.pop() qs.append(p) t = len(qs) for i in range(L-2, -1, -1): p = ps[i] while len(qs) > t and cross3(qs[-1], qs[-2], p) > 0: qs.pop() qs.append(p) return qs A = [] IN = [-1]*N OUT = [-1]*N dfs(0, -1, 0) ・Auxiliary Tree class AuxiliaryTree: def __init__(self, N, G): self.N = N self.G = G self.IN = [0]*N self.OUT = [0]*N self.depth = [0]*N self.E = [] self.dfs(0, -1, 0) self.L = len(self.E) self.lg = [0]*(self.L+1) self.make_sparse_table() @bootstrap def dfs(self, n, p, d): self.depth[n] = d self.IN[n] = len(self.E) self.E.append(n) for v in self.G[n]: if v == p: continue yield self.dfs(v, n, d+1) self.E.append(n) self.OUT[n] = len(self.E) self.E.append(n) yield def make_sparse_table(self): for i in range(2, self.L+1): self.lg[i] = self.lg[i>>1]+1 self.st = [[self.L]*(self.L-(1< (D+1)//2: idx = pre[idx] center1 = idx center2 = -1 if D%2 == 1: center2 = pre[idx] ・高速フーリエ変換 class FFT(): def primitive_root_constexpr(self,m): if m==2:return 1 if m==167772161:return 3 if m==469762049:return 3 if m==754974721:return 11 if m==998244353:return 3 divs=[0]*20 divs[0]=2 cnt=1 x=(m-1)//2 while(x%2==0):x//=2 i=3 while(i*i<=x): if (x%i==0): divs[cnt]=i cnt+=1 while(x%i==0): x//=i i+=2 if x>1: divs[cnt]=x cnt+=1 g=2 while(1): ok=True for i in range(cnt): if pow(g,(m-1)//divs[i],m)==1: ok=False break if ok: return g g+=1 def bsf(self,x): res=0 while(x%2==0): res+=1 x//=2 return res rank2=0 root=[] iroot=[] rate2=[] irate2=[] rate3=[] irate3=[] def __init__(self,MOD): self.mod=MOD self.g=self.primitive_root_constexpr(self.mod) self.rank2=self.bsf(self.mod-1) self.root=[0 for i in range(self.rank2+1)] self.iroot=[0 for i in range(self.rank2+1)] self.rate2=[0 for i in range(self.rank2)] self.irate2=[0 for i in range(self.rank2)] self.rate3=[0 for i in range(self.rank2-1)] self.irate3=[0 for i in range(self.rank2-1)] self.root[self.rank2]=pow(self.g,(self.mod-1)>>self.rank2,self.mod) self.iroot[self.rank2]=pow(self.root[self.rank2],self.mod-2,self.mod) for i in range(self.rank2-1,-1,-1): self.root[i]=(self.root[i+1]**2)%self.mod self.iroot[i]=(self.iroot[i+1]**2)%self.mod prod=1;iprod=1 for i in range(self.rank2-1): self.rate2[i]=(self.root[i+2]*prod)%self.mod self.irate2[i]=(self.iroot[i+2]*iprod)%self.mod prod=(prod*self.iroot[i+2])%self.mod iprod=(iprod*self.root[i+2])%self.mod prod=1;iprod=1 for i in range(self.rank2-2): self.rate3[i]=(self.root[i+3]*prod)%self.mod self.irate3[i]=(self.iroot[i+3]*iprod)%self.mod prod=(prod*self.iroot[i+3])%self.mod iprod=(iprod*self.root[i+3])%self.mod def butterfly(self,a): n=len(a) h=(n-1).bit_length() LEN=0 while(LEN self.bottom*other.top def __ge__(self, other): other = self._to_fraction(other) return self.top*other.bottom >= self.bottom*other.top def __add__(self, other): other = self._to_fraction(other) GCD = gcd(self.bottom, other.bottom) bottom = self.bottom*other.bottom//GCD top1 = self.top*(bottom//self.bottom) top2 = other.top*(bottom//other.bottom) return Fraction(top1+top2, bottom) def __sub__(self, other): other = self._to_fraction(other) GCD = gcd(self.bottom, other.bottom) bottom = self.bottom*other.bottom//GCD top1 = self.top*(bottom//self.bottom) top2 = other.top*(bottom//other.bottom) return Fraction(top1-top2, bottom) def __mul__(self, other): other = self._to_fraction(other) return Fraction(self.top*other.top, self.bottom*other.bottom) def __truediv__(self, other): other = self._to_fraction(other) return Fraction(self.top*other.bottom, self.bottom*other.top) def __radd__(self, other): return self+other def __rsub__(self, other): return self._to_fraction(other)-self def __rmul__(self, other): return self*other def __rtruediv__(self, other): return self._to_fraction(other)/self def __int__(self): return self.top//self.bottom def __float__(self): return self.top/self.bottom def __str__(self): return f"{self.top}/{self.bottom}" def __hash__(self): return hash((self.top, self.bottom)) def __bool__(self): return self.top != 0 def __neg__(self): return Fraction(-self.top, self.bottom) def __abs__(self): return Fraction(abs(self.top), self.bottom) def get(self): return self.top, self.bottom ・ランダムチェッカー from random import randrange def solve1(N, L, R, A): # 自分のプログラム pass def solve2(N, L, R, A): # 愚直のプログラム pass while True: # 入力データをランダムで生成する # 違う出力をした場合に出力して終了する if solve1() != solve2(): break ・floor_sum def floor_sum(n, m, a, b): ans = 0 while True: if a >= m or a < 0: ans += n * (n - 1) * (a // m) // 2 a %= m if b >= m or b < 0: ans += n * (b // m) b %= m y_max = a * n + b if y_max < m: break n, b, m, a = y_max // m, y_max % m, a, m return ans ・ハッシュ(Hack対策) from random import randrange H = randrange(1, 1<<60) def Hash(n): return n^H ・ChatGPT 翻訳依頼 今からCodeforcesの問題文を与えるため、日本語に翻訳してください。 翻訳以外の用途で生成AIを使用することは禁止されているため、問題の解法については出力しないようにしてください。 ・Convex Hull Trick(O(N)) class Convex_Hull_Trick: def __init__(self): self.que = deque() def check(self, f1, f2, f3): return (f2[0] - f1[0]) * (f3[1] - f2[1]) >= (f2[1] - f1[1]) * (f3[0] - f2[0]) def f(self, f1, x): return f1[0]*x + f1[1] # add f_i(x) = a*x + b # 降順 def add_line(self, a, b): f1 = (a, b) while len(self.que) >= 2 and self.check(self.que[-2], self.que[-1], f1): self.que.pop() self.que.append(f1) # min f_i(x) # 昇順 def query(self, x): while len(self.que) >= 2 and self.f(self.que[0], x) >= self.f(self.que[1], x): self.que.popleft() return self.f(self.que[0], x) ・セグメント木(区間演算、一点取得) def op(x, y): return class SegTree: def __init__(self, init_val, op, ide_ele): n = len(init_val) self.n = n self.op = op self.ide_ele = ide_ele self.num = 1 << (n - 1).bit_length() self.tree = [ide_ele] * 2 * self.num for i in range(n): self.tree[self.num + i] = init_val[i] def update(self, l, r, x): l += self.num r += self.num while l < r: if l & 1: self.tree[l] = self.op(x, self.tree[l]) l += 1 if r & 1: self.tree[r - 1] = self.op(x, self.tree[r - 1]) l >>= 1 r >>= 1 def query(self, k): ans = self.ide_ele k += self.num ans = self.op(ans, self.tree[k]) while k > 1: ans = self.op(ans, self.tree[k>>1]) k >>= 1 return ans ・セグメント木(区間更新、一点取得) class SegTree: def __init__(self, init_val): n = len(init_val) self.n = n self.num = 1 << (n - 1).bit_length() self.tree = [-1] * 2 * self.num self.rank = [-1] * 2 * self.num self.cnt = 0 for i in range(n): self.tree[self.num + i] = init_val[i] self.rank[self.num + i] = self.cnt self.cnt += 1 def update(self, l, r, x): l += self.num r += self.num while l < r: if l & 1: self.tree[l] = x self.rank[l] = self.cnt l += 1 if r & 1: self.tree[r - 1] = x self.rank[r - 1] = self.cnt l >>= 1 r >>= 1 self.cnt += 1 def query(self, k): k += self.num ans = self.tree[k] MAX = self.rank[k] while k > 1: if MAX < self.rank[k>>1]: ans = self.tree[k>>1] MAX = self.rank[k>>1] k >>= 1 return ans ・区間XOR def XORSUM(n): if n%4 == 0: return n elif n%4 == 1: return 1 elif n%4 == 2: return n+1 else: return 0 def rangeXOR(l, r): return XORSUM(r)^XORSUM(l-1) ・BucketList(tatyam) # https://github.com/tatyam-prime/SortedSet/blob/main/BucketList.py import math from typing import Generic, Iterable, Iterator, TypeVar T = TypeVar('T') class BucketList(Generic[T]): BUCKET_RATIO = 16 SPLIT_RATIO = 24 def __init__(self, a: Iterable[T] = []) -> None: a = list(a) n = self.size = len(a) num_bucket = int(math.ceil(math.sqrt(n / self.BUCKET_RATIO))) self.a = [a[n * i // num_bucket : n * (i + 1) // num_bucket] for i in range(num_bucket)] def __iter__(self) -> Iterator[T]: for i in self.a: for j in i: yield j def __reversed__(self) -> Iterator[T]: for i in reversed(self.a): for j in reversed(i): yield j def __eq__(self, other) -> bool: if len(self) != len(other): return False for x, y in zip(self, other): if x != y: return False return True def __len__(self) -> int: return self.size def __repr__(self) -> str: return "BucketList" + str(self.a) def __str__(self) -> str: return str(list(self)) def __contains__(self, x: T) -> bool: "Return True if x is in the bucket list. / O(N)" for y in self: if x == y: return True return False def _insert(self, a: list[T], b: int, i: int, x: T) -> None: a.insert(i, x) self.size += 1 if len(a) > len(self.a) * self.SPLIT_RATIO: mid = len(a) >> 1 self.a[b:b+1] = [a[:mid], a[mid:]] def insert(self, i: int, x: T) -> None: "Insert x at the i-th position. / O(√N)" if self.size == 0: if i != 0 and i != -1: raise IndexError self.a = [[x]] self.size = 1 return if i < 0: for b, a in enumerate(reversed(self.a)): i += len(a) if i >= 0: return self._insert(a, len(self.a) + ~b, i, x) else: for b, a in enumerate(self.a): if i <= len(a): return self._insert(a, b, i, x) i -= len(a) raise IndexError def append(self, x: T) -> None: "Append x to the end of the list. / amortized O(1)" if self.size == 0: self.a = [[x]] self.size = 1 return a = self.a[-1] return self._insert(a, len(self.a) - 1, len(a), x) def extend(self, a: Iterable[T]) -> None: for x in a: self.append(x) def __getitem__(self, i: int) -> T: if i < 0: for a in reversed(self.a): i += len(a) if i >= 0: return a[i] else: for a in self.a: if i < len(a): return a[i] i -= len(a) raise IndexError def _pop(self, a: list[T], b: int, i: int) -> T: ans = a.pop(i) self.size -= 1 if not a: del self.a[b] return ans def pop(self, i: int = -1) -> T: "Remove and return the i-th element. / O(√N) / O(-i) if i < 0" if i < 0: for b, a in enumerate(reversed(self.a)): i += len(a) if i >= 0: return self._pop(a, ~b, i) else: for b, a in enumerate(self.a): if i < len(a): return self._pop(a, b, i) i -= len(a) raise IndexError def count(self, x: T) -> int: "Return the number of occurrences of x. / O(N)" return sum(1 for y in self if x == y) def index(self, x: T) -> int: "Return the index of the first occurrence of x, raise ValueError if not found. / O(N)" for i, y in enumerate(self): if x == y: return i raise ValueError def remove(self, x: T) -> None: "Remove the first occurrence of x, raise ValueError if not found. / O(N)" self.pop(self.index(x)) def clear(self) -> None: self.a = [] self.size = 0 def reverse(self) -> None: self.a.reverse() for a in self.a: a.reverse() def copy(self) -> 'BucketList[T]': return BucketList(self) ・Lucasの定理 class Lucas: def __init__(self, MOD): self.MOD = MOD self.comb = [[0]*self.MOD for _ in range(self.MOD)] self.comb[0][0] = 1 for i in range(1, self.MOD): self.comb[i][0] = 1 for j in range(1, i+1): self.comb[i][j] = (self.comb[i-1][j-1]+self.comb[i-1][j])%self.MOD def C(self, N, K): ans = 1 while N: ans *= self.comb[N%self.MOD][K%self.MOD] ans %= self.MOD N //= self.MOD K //= self.MOD return ans ・ラベル class Label: def __init__(self, N, A=[]): self.N = N if A: self.label = A[:] else: self.label = list(range(N)) self.labelR = [-1]*self.N for i in range(self.N): self.labelR[self.label[i]] = i # ラベルがどの箱に貼られているか def __getitem__(self, n): return self.labelR[n] # 箱に貼られているラベルは何か def getLabel(self, n): return self.label[n] # ラベルの情報を元にスワップ def swap(self, l, r): self.label[self.labelR[l]], self.label[self.labelR[r]] = self.label[self.labelR[r]], self.label[self.labelR[l]] self.labelR[l], self.labelR[r] = self.labelR[r], self.labelR[l] ・動的セグメントツリー class Node: def __init__(self, init_val): self.left = None self.right = None self.value = init_val def op(x, y): if x[0]%2 == 0: return (x[0]+y[0], x[1]+y[1], x[2]+y[2]) else: return (x[0]+y[0], x[1]+y[2], x[2]+y[1]) class DynamicSegmentTree: def __init__(self, L, R, op, init_val): self.root = Node(init_val) self.L = L self.R = R self.op = op self.init_val = init_val def update(self, idx, value): stack = [(self.root, self.L, self.R)] path = [] while stack: node, s, e = stack.pop() path.append((node, s, e)) if s == e: node.value = value break mid = (s+e)//2 if idx <= mid: if not node.left: node.left = Node(self.init_val) stack.append((node.left, s, mid)) else: if not node.right: node.right = Node(self.init_val) stack.append((node.right, mid+1, e)) path.pop() while path: node, s, e = path.pop() node.value = self.op(node.left.value if node.left else self.init_val, node.right.value if node.right else self.init_val) def query(self, l, r): stack = [(self.root, self.L, self.R)] ans = self.init_val while stack: node, s, e = stack.pop() if not node or e < l or r < s: continue if l <= s and e <= r: ans = self.op(ans, node.value) continue mid = (s+e)//2 stack.append((node.left, s, mid)) stack.append((node.right, mid+1, e)) return ans ・最大流 # https://github.com/not522/ac-library-python/blob/master/atcoder/maxflow.py from typing import NamedTuple, Optional, List, cast class MFGraph: class Edge(NamedTuple): src: int dst: int cap: int flow: int class _Edge: def __init__(self, dst: int, cap: int) -> None: self.dst = dst self.cap = cap self.rev: Optional[MFGraph._Edge] = None def __init__(self, n: int) -> None: self._n = n self._g: List[List[MFGraph._Edge]] = [[] for _ in range(n)] self._edges: List[MFGraph._Edge] = [] def add_edge(self, src: int, dst: int, cap: int) -> int: assert 0 <= src < self._n assert 0 <= dst < self._n assert 0 <= cap m = len(self._edges) e = MFGraph._Edge(dst, cap) re = MFGraph._Edge(src, 0) e.rev = re re.rev = e self._g[src].append(e) self._g[dst].append(re) self._edges.append(e) return m def get_edge(self, i: int) -> Edge: assert 0 <= i < len(self._edges) e = self._edges[i] re = cast(MFGraph._Edge, e.rev) return MFGraph.Edge( re.dst, e.dst, e.cap + re.cap, re.cap ) def edges(self) -> List[Edge]: return [self.get_edge(i) for i in range(len(self._edges))] def change_edge(self, i: int, new_cap: int, new_flow: int) -> None: assert 0 <= i < len(self._edges) assert 0 <= new_flow <= new_cap e = self._edges[i] e.cap = new_cap - new_flow assert e.rev is not None e.rev.cap = new_flow def flow(self, s: int, t: int, flow_limit: Optional[int] = None) -> int: assert 0 <= s < self._n assert 0 <= t < self._n assert s != t if flow_limit is None: flow_limit = cast(int, sum(e.cap for e in self._g[s])) current_edge = [0] * self._n level = [0] * self._n def fill(arr: List[int], value: int) -> None: for i in range(len(arr)): arr[i] = value def bfs() -> bool: fill(level, self._n) queue = [] q_front = 0 queue.append(s) level[s] = 0 while q_front < len(queue): v = queue[q_front] q_front += 1 next_level = level[v] + 1 for e in self._g[v]: if e.cap == 0 or level[e.dst] <= next_level: continue level[e.dst] = next_level if e.dst == t: return True queue.append(e.dst) return False def dfs(lim: int) -> int: stack = [] edge_stack: List[MFGraph._Edge] = [] stack.append(t) while stack: v = stack[-1] if v == s: flow = min(lim, min(e.cap for e in edge_stack)) for e in edge_stack: e.cap -= flow assert e.rev is not None e.rev.cap += flow return flow next_level = level[v] - 1 while current_edge[v] < len(self._g[v]): e = self._g[v][current_edge[v]] re = cast(MFGraph._Edge, e.rev) if level[e.dst] != next_level or re.cap == 0: current_edge[v] += 1 continue stack.append(e.dst) edge_stack.append(re) break else: stack.pop() if edge_stack: edge_stack.pop() level[v] = self._n return 0 flow = 0 while flow < flow_limit: if not bfs(): break fill(current_edge, 0) while flow < flow_limit: f = dfs(flow_limit - flow) flow += f if f == 0: break return flow def min_cut(self, s: int) -> List[bool]: visited = [False] * self._n stack = [s] visited[s] = True while stack: v = stack.pop() for e in self._g[v]: if e.cap > 0 and not visited[e.dst]: visited[e.dst] = True stack.append(e.dst) return visited ・Gauss-Jordanの掃き出し法(MOD) def GaussJordan_MOD(A, is_extended): H, W = len(A), len(A[0]) for i in range(H): for j in range(W): A[i][j] %= MOD rank = 0 for j in range(W-is_extended): for i in range(rank, H): if A[i][j] != 0: pivot = i break else: continue A[pivot], A[rank] = A[rank], A[pivot] inv = pow(A[rank][j], -1, MOD) for i in range(W): A[rank][i] *= inv A[rank][i] %= MOD for i in range(H): if i == rank or A[i][j] == 0: continue mul = A[i][j] for k in range(W): A[i][k] -= A[rank][k]*mul%MOD A[i][k] %= MOD rank += 1 return rank ・連立方程式(GaussJordan) def linear_equation(A, B): H, W = len(A), len(A[0]) for i in range(H): A[i].append(B[i]) rank = GaussJordan_MOD(A, True) for i in range(rank, H): if A[i][W]: return None, None ans = [A[i][W] for i in range(rank)] C = A[:rank] for i in range(rank): C[i].pop() return C, ans ・二項係数(Nが大きいとき用) def C(N, K): if N < K: return 0 if N < K*2: K = N-K top = 1 for i in range(N, N-K, -1): top *= i top %= MOD bottom = 1 for i in range(1, K+1): bottom *= i bottom %= MOD return inverse(top, bottom) ・BinaryTrie class BinaryTrie: def __init__(self, length): self.tree = [[-1]*2] self.arrive = [0] self.last = [0] self.nex = 1 self.length = length self.all_xor = 0 def BIT(self, bit, n): return bit>>n & 1 def operate_xor(self, n): self.all_xor ^= n def count(self): return self.arrive[0] def find(self, n): now = 0 for i in reversed(range(self.length)): nex = self.BIT(n, i)^self.BIT(self.all_xor, i) if self.tree[now][nex] != -1 and 1 <= self.arrive[self.tree[now][nex]]: now = self.tree[now][self.BIT(n, i)^self.BIT(self.all_xor, i)] else: return False return True def insert(self, n): now = 0 self.arrive[now] += 1 for i in reversed(range(self.length)): if self.tree[now][self.BIT(n, i)^self.BIT(self.all_xor, i)] == -1: self.tree[now][self.BIT(n, i)^self.BIT(self.all_xor, i)] = self.nex self.tree.append([-1]*2) self.arrive.append(0) self.last.append(0) now = self.nex self.nex += 1 else: now = self.tree[now][self.BIT(n, i)^self.BIT(self.all_xor, i)] self.arrive[now] += 1 self.last[now] += 1 def delete(self, n): # 存在していることが前提 now = 0 self.arrive[now] -= 1 for i in reversed(range(self.length)): now = self.tree[now][self.BIT(n, i)^self.BIT(self.all_xor, i)] self.arrive[now] -= 1 self.last[now] -= 1 def min_element(self, n=0): if self.count() == 0: return -1 ans = 0 now = 0 for i in reversed(range(self.length)): bit = self.BIT(n, i)^self.BIT(self.all_xor, i) nex = self.tree[now][bit] if nex != -1 and 1 <= self.arrive[nex]: now = nex if bit: ans |= 1< y.key def meld(self, x, y): if not x or not y: return x if x else y if not self._comp(x, y): x, y = y, x x.l = self.meld(x.l, y) x.l, x.r = x.r, x.l return x def alloc(self, key): return self.Node(key) def push(self, x, key): return self.meld(x, self.alloc(key)) def pop(self, x): return self.meld(x.l, x.r) ・SkewHeap(遅延加算付き) class SkewHeap: class Node: __slots__ = ("key", "laz", "l", "r") def __init__(self, key): self.key = key self.laz = 0 self.l = None self.r = None def __init__(self, isMin=True): self.isMin = isMin def _propagate(self, x): if x.laz == 0: return if x.l: x.l.laz += x.laz if x.r: x.r.laz += x.laz x.key += x.laz x.laz = 0 def _comp(self, x, y): if self.isMin: return x.key+x.laz < y.key+y.laz else: return x.key+x.laz > y.key+y.laz def meld(self, x, y): if not x or not y: return x if x else y if not self._comp(x, y): x, y = y, x self._propagate(x) x.l = self.meld(x.l, y) x.l, x.r = x.r, x.l return x def alloc(self, key): return self.Node(key) def push(self, x, key): return self.meld(x, self.alloc(key)) def pop(self, x): self._propagate(x) return self.meld(x.l, x.r) def top(self, x): self._propagate(x) return x.key def apply(self, x, laz): x.laz += laz self._propagate(x) ・Cartesian Tree def CartesianTree(P): stack = [] child = [[-1]*2 for _ in range(N)] parent = [-1]*N for i in range(N): s = -1 while stack and P[i] < P[stack[-1]]: s = stack.pop() child[i][0] = s if s != -1: parent[s] = i if stack: parent[i] = stack[-1] child[stack[-1]][1] = i stack.append(i) L, R = [-1]*N, [-1]*N stack = [(P.index(min(P)), 0, N-1)] while stack: n, l, r = stack.pop() L[n] = l R[n] = r a, b = child[n] if a != -1: stack.append((a, l, n-1)) if b != -1: stack.append((b, n+1, r)) return child, parent, L, R ・ラグランジュ補間(係数) def lagrange_interpolation_coef(X, Y): N = len(X)-1 for i in range(N+1): t = 1 for j in range(N+1): if i != j: t *= X[i]-X[j] t %= MOD Y[i] *= inverse(1, t) Y[i] %= MOD dp = [0]*(N+2) dp[0] = 1 for i in range(N+1): ndp = [0]*(N+2) for j in range(N+2): ndp[j] += dp[j]*(-X[i])%MOD ndp[j] %= MOD if 1 <= j: ndp[j] += dp[j-1] ndp[j] %= MOD dp = ndp ans = [0]*(N+1) invX = [] for i in range(N+1): if X[i] != 0: invX.append(inverse(1, X[i])) else: invX.append(0) for i in range(N+1): if Y[i] == 0: continue if X[i] == 0: for j in range(N+1): ans[j] += dp[j+1]*Y[i]%MOD ans[j] %= MOD else: ans[0] -= dp[0]*invX[i]%MOD*Y[i]%MOD ans[0] %= MOD pre = (-dp[0])*invX[i]%MOD for j in range(1, N+1): ans[j] -= (dp[j]-pre)*invX[i]%MOD*Y[i]%MOD ans[j] %= MOD pre = ((-(dp[j]-pre))*invX[i])%MOD return ans ・ラグランジュ補間 def lagrange_interpolation(X, Y, T): N = len(X)-1 ans = 0 for i in range(N+1): t = 1 t_inv = 1 for j in range(N+1): if i == j: continue t *= T-X[j] t %= MOD t_inv *= X[i]-X[j] t_inv %= MOD ans += inverse(t, t_inv)*Y[i]%MOD ans %= MOD return ans ・ラグランジュ補間(等差数列) def lagrange_interpolation_arithmetic(a, d, Y, T): # a = Xの初項 d = Xの交差 N = len(Y)-1 ans = 0 ft = 1 for i in range(N+1): ft *= (T-(a+d*i))%MOD ft %= MOD f = 1 for i in range(1, N+1): f *= -i*d%MOD f %= MOD ans += inverse(Y[0]*ft%MOD, f*(T-a)%MOD) ans %= MOD for i in range(N): f *= inverse(d*(i+1), d*(i-N)) f %= MOD ans += inverse(Y[i+1]*ft%MOD, f*(T-a-d*(i+1))%MOD) ans %= MOD return ans ・FPS # https://github.com/atcoder/ac-library/blob/master/atcoder/convolution.hpp from array import array MOD = 998244353 IMAG = 911660635 IIMAG = 86583718 INV2 = 499122177 rate2 = array( "I", [ 0, 911660635, 509520358, 369330050, 332049552, 983190778, 123842337, 238493703, 975955924, 603855026, 856644456, 131300601, 842657263, 730768835, 942482514, 806263778, 151565301, 510815449, 503497456, 743006876, 741047443, 56250497, 867605899, 0, ], ) irate2 = array( "I", [ 0, 86583718, 372528824, 373294451, 645684063, 112220581, 692852209, 155456985, 797128860, 90816748, 860285882, 927414960, 354738543, 109331171, 293255632, 535113200, 308540755, 121186627, 608385704, 438932459, 359477183, 824071951, 103369235, 0, ], ) rate3 = array( "I", [ 0, 372528824, 337190230, 454590761, 816400692, 578227951, 180142363, 83780245, 6597683, 70046822, 623238099, 183021267, 402682409, 631680428, 344509872, 689220186, 365017329, 774342554, 729444058, 102986190, 128751033, 395565204, 0, ], ) irate3 = array( "I", [ 0, 509520358, 929031873, 170256584, 839780419, 282974284, 395914482, 444904435, 72135471, 638914820, 66769500, 771127074, 985925487, 262319669, 262341272, 625870173, 768022760, 859816005, 914661783, 430819711, 272774365, 530924681, 0, ], ) # https://judge.yosupo.jp/submission/55648 def butterfly(a: list): n = len(a) h = (n - 1).bit_length() le = 0 while le < h: if h - le == 1: p = 1 << (h - le - 1) rot = 1 for s in range(1 << le): offset = s << (h - le) for i in range(p): l = a[i + offset] r = a[i + offset + p] * rot a[i + offset] = (l + r) % MOD a[i + offset + p] = (l - r) % MOD rot *= rate2[(~s & -~s).bit_length()] rot %= MOD le += 1 else: p = 1 << (h - le - 2) rot = 1 for s in range(1 << le): rot2 = rot * rot % MOD rot3 = rot2 * rot % MOD offset = s << (h - le) for i in range(p): a0 = a[i + offset] a1 = a[i + offset + p] * rot a2 = a[i + offset + p * 2] * rot2 a3 = a[i + offset + p * 3] * rot3 a1na3imag = (a1 - a3) % MOD * IMAG a[i + offset] = (a0 + a2 + a1 + a3) % MOD a[i + offset + p] = (a0 + a2 - a1 - a3) % MOD a[i + offset + p * 2] = (a0 - a2 + a1na3imag) % MOD a[i + offset + p * 3] = (a0 - a2 - a1na3imag) % MOD rot *= rate3[(~s & -~s).bit_length()] rot %= MOD le += 2 def butterfly_inv(a: list): n = len(a) h = (n - 1).bit_length() le = h while le: if le == 1: p = 1 << (h - le) irot = 1 for s in range(1 << (le - 1)): offset = s << (h - le + 1) for i in range(p): l = a[i + offset] r = a[i + offset + p] a[i + offset] = (l + r) % MOD a[i + offset + p] = (l - r) * irot % MOD irot *= irate2[(~s & -~s).bit_length()] irot %= MOD le -= 1 else: p = 1 << (h - le) irot = 1 for s in range(1 << (le - 2)): irot2 = irot * irot % MOD irot3 = irot2 * irot % MOD offset = s << (h - le + 2) for i in range(p): a0 = a[i + offset] a1 = a[i + offset + p] a2 = a[i + offset + p * 2] a3 = a[i + offset + p * 3] a2na3iimag = (a2 - a3) * IIMAG % MOD a[i + offset] = (a0 + a1 + a2 + a3) % MOD a[i + offset + p] = (a0 - a1 + a2na3iimag) * irot % MOD a[i + offset + p * 2] = (a0 + a1 - a2 - a3) * irot2 % MOD a[i + offset + p * 3] = (a0 - a1 - a2na3iimag) * irot3 % MOD irot *= irate3[(~s & -~s).bit_length()] irot %= MOD le -= 2 def intt(a): if len(a) <= 1: return butterfly_inv(a) iv = pow(len(a), MOD - 2, MOD) for i, x in enumerate(a): a[i] = x * iv % MOD def multiply(s: list, t: list): n = len(s) m = len(t) if min(n, m) <= 60: a = [0] * (n + m - 1) for i in range(n): if i & 0b111 == 0: for j in range(m): a[i + j] += s[i] * t[j] a[i + j] %= MOD else: for j in range(m): a[i + j] += s[i] * t[j] return [x % MOD for x in a] a = s.copy() b = t.copy() z = 1 << (n + m - 2).bit_length() a += [0] * (z - n) b += [0] * (z - m) butterfly(a) butterfly(b) for i in range(z): a[i] *= b[i] a[i] %= MOD butterfly_inv(a) a = a[: n + m - 1] iz = pow(z, MOD - 2, MOD) return [v * iz % MOD for v in a] def pow2(s: list): n = len(s) l = (n << 1) - 1 if n <= 60: a = [0] * l for i, x in enumerate(s): for j, y in enumerate(s): a[i + j] += x * y return [x % MOD for x in a] z = 1 << (l - 1).bit_length() a = s + [0] * (z - n) butterfly(a) for i, x in enumerate(a): a[i] = x * x % MOD butterfly_inv(a) a[l:] = [] iz = pow(z, MOD - 2, MOD) return [x * iz % MOD for x in a] def shrink(a: list): while a and not a[-1]: a.pop() def fps_add(a: list, b: list): if len(a) < len(b): res = b.copy() for i, x in enumerate(a): res[i] += x else: res = a.copy() for i, x in enumerate(b): res[i] += x return [x - MOD if MOD <= x else x for x in res] def fps_sub(a: list, b: list): if len(a) < len(b): res = b.copy() for i, x in enumerate(a): res[i] -= x return [MOD - x if 0 < x else -x for x in res] else: res = a.copy() for i, x in enumerate(b): res[i] -= x return [x if 0 <= x else x + MOD for x in res] def fps_neg(a: list): return [MOD - x if x else 0 for x in a] def fps_div(a: list, b: list) -> list: if len(a) < len(b): return [] n = len(a) - len(b) + 1 cnt = 0 if len(b) > 64: return multiply(a[::-1][:n], fps_inv(b[::-1], n))[:n][::-1] f = a.copy() g = b.copy() while g and not g[-1]: g.pop() cnt += 1 coef = pow(g[-1], MOD - 2, MOD) g = [x * coef % MOD for x in g] deg = len(f) - len(g) + 1 lg = len(g) res = [0] * deg for i in reversed(range(deg)): res[i] = x = f[i + lg - 1] % MOD for j, y in enumerate(g): f[i + j] -= x * y return [x * coef % MOD for x in res] + [0] * cnt def fps_mod(a: list, b: list) -> list: res = fps_sub(a, multiply(fps_div(a, b), b)) while res and not res[-1]: res.pop() return res def fps_divmod(a: list, b: list): q = fps_div(a, b) r = fps_sub(a, multiply(q, b)) while r and not r[-1]: r.pop() return q, r def fps_eval(a: list, x: int) -> int: r = 0 w = 1 for v in a: r += w * v % MOD w = w * x % MOD return r % MOD def fps_inv(a: list, deg: int = -1): # assert(self[0] != 0) if deg == -1: deg = len(a) res = [0] * deg res[0] = pow(a[0], MOD - 2, MOD) d = 1 iv = INV2 while d < deg: d2 = d << 1 f = [0] * d2 fl = min(len(a), d2) f[:fl] = a[:fl] g = [0] * d2 g[:d] = res[:d] butterfly(g) butterfly(f) for i in range(d2): f[i] = f[i] * g[i] % MOD butterfly_inv(f) f[:d] = [0] * d for i in range(d, d2): f[i] = f[i] * iv % MOD butterfly(f) for i in range(d2): f[i] = f[i] * g[i] % MOD butterfly_inv(f) for i in range(d, min(d2, deg)): res[i] = (MOD - f[i]) * iv % MOD d <<= 1 iv = iv * INV2 % MOD return res def fps_pow(a: list, k: int, deg=-1) -> list: n = len(a) if deg == -1: deg = n if k == 0: if not deg: return [] res = [0] * deg res[0] = 1 return res for i, x in enumerate(a): if x: iz = pow(x, MOD - 2, MOD) res = [x * iz % MOD for x in a[i:]] res = fps_log(res, deg) res = [x * k % MOD for x in res] res = fps_exp(res, deg) coef = pow(x, k, MOD) res = [0] * (i * k) + [x * coef % MOD for x in res] if len(res) < deg: return res + [0] * (deg - len(res)) return res[:deg] if (i + 1) * k >= deg: break return [0] * deg def fps_exp(a: list, deg: int = -1): # assert a[0] == 0 if deg == -1: deg = len(a) inv = [0, 1] def inplace_integral(f: list) -> list: n = len(f) while len(inv) <= n: j, k = divmod(MOD, len(inv)) inv.append((-inv[k] * j) % MOD) return [0] + [x * inv[i + 1] % MOD for i, x in enumerate(f)] b = [1, (a[1] if 1 < len(a) else 0)] c = [1] z1 = [] z2 = [1, 1] m = 2 while m < deg: y = b + [0] * m butterfly(y) z1 = z2 z = [y[i] * p % MOD for i, p in enumerate(z1)] intt(z) z[: m >> 1] = [0] * (m >> 1) butterfly(z) for i, p in enumerate(z1): z[i] = z[i] * (-p) % MOD intt(z) c[m >> 1 :] = z[m >> 1 :] z2 = c + [0] * m butterfly(z2) tmp = min(len(a), m) x = a[:tmp] + [0] * (m - tmp) x = fps_diff(x) x.append(0) butterfly(x) for i, p in enumerate(x): x[i] = y[i] * p % MOD intt(x) for i, p in enumerate(b): if not i: continue x[i - 1] -= p * i % MOD x += [0] * m for i in range(m - 1): x[m + i], x[i] = x[i], 0 butterfly(x) for i, p in enumerate(z2): x[i] = x[i] * p % MOD intt(x) x.pop() x = inplace_integral(x) x[:m] = [0] * m for i in range(m, min(len(a), m << 1)): x[i] += a[i] butterfly(x) for i, p in enumerate(y): x[i] = x[i] * p % MOD intt(x) b[m:] = x[m:] m <<= 1 return b[:deg] def fps_log(a: list, deg=-1) -> list: # assert(a[0] == 1) if deg == -1: deg = len(a) return fps_integral(multiply(fps_diff(a), fps_inv(a, deg))[: deg - 1]) def fps_integral(a: list): n = len(a) res = [0] * (n + 1) if n: res[1] = 1 for i in range(2, n + 1): j, k = divmod(MOD, i) res[i] = (-res[k] * j) % MOD for i, x in enumerate(a): res[i + 1] = res[i + 1] * x % MOD return res def fps_diff(a: list): return [i * x % MOD for i, x in enumerate(a) if i] def LinearRecurrence(n: int, p: list, q: list): """ [x^n]P(x)/Q(x) """ # assert len(p) < len(q) shrink(q) while n: q2 = q[:] for i in range(1, len(q2), 2): q2[i] = (-q2[i]) % MOD s = multiply(p, q2) t = multiply(q, q2) for i in range(n & 1, len(s), 2): p[i >> 1] = s[i] for i in range(0, len(t), 2): q[i >> 1] = t[i] n >>= 1 return p[0] * pow(q[0], -1, MOD) % MOD def fps_compsite(f: list, g: list): """ g(f(x)) mod x^(n+1) """ # assert len(f) == len(g) def trans_multiply(s, t): n = len(s) m = len(t) l = 1 << (max(n, m) - 1).bit_length() a = s + [0] * (l - n) b = t + [0] * (l - m) a = [a[0]] + [a[l - i] for i in range(1, l)] butterfly(a) butterfly(b) iz = pow(l, MOD - 2, MOD) for i, x in enumerate(b): a[i] = a[i] * x % MOD * iz % MOD butterfly_inv(a) a = [a[0]] + [a[l - i] for i in range(1, l)] return a[: n - m + 1] n = len(f) l = 1 << (n - 1).bit_length() a = [MOD - x for x in f] + [0] * (2 * l - n) b = g + [0] * (l - n) def _inner(q, n, k): if n == 1: p = [0] * (2 * k) p[0::2] = b[::-1] return p siz = 2 * n * k r = [MOD - x if i & 1 else x for i, x in enumerate(q)] qq = multiply(q, r) qq.append(0) for i in range(0, siz, 2): qq[siz + i] = (qq[siz + i] + 2 * q[i]) % MOD nq = [0] * siz for j in range(2 * k): for i in range(n >> 1): nq[n * j + i] = qq[2 * n * j + 2 * i] np = _inner(nq, n >> 1, k << 1) pq = [0] * (2 * siz) for j in range(2 * k): for i in range(n >> 1): pq[2 * n * j + 2 * i + 1] = np[n * j + i] p = [pq[siz + i] for i in range(siz)] pq.pop() x = trans_multiply(pq, r) p = [(p[i] + x[i]) % MOD for i in range(siz)] return p p = _inner(a, l, 1) p = p[l - 1 :: -1] return p[:n] def fps_compsitional_inv(calc, deg): """ input calc(g, d) = f(g(x)) mod x^d を計算する fps_compsite よりも良い計算量のものが存在するならば書く output g(x) mod x^deg s.t. f(g(x)) = x """ c = deg.bit_length() g = calc([0, 1], 2) g[1] = pow(g[1], -1, MOD) d = 2 for i in range(c): d <<= 1 fg = calc(g, d + 1) fdg = multiply(fps_diff(fg), fps_inv(fps_diff(g))) fdg[d:] = [] fg[1] -= 1 g = fps_sub(g, multiply(fg, fps_inv(fdg))) g[d:] = [] g[deg:] = [] return g INVMOD = [1, 1] def SubsetSum(d: list) -> list: """ prod ( 1 + x^i ) ^ d[i] d[w] = 重さ w が d[w] 種類 1 個ずつある r[w] = 重さが w になるような選び方 """ n = len(d) for i in range(len(INVMOD), n): INVMOD.append(-INVMOD[MOD % i] * (MOD // i) % MOD) res = [0] * n sgn = [-1, 1] for i in range(1, n): if d[i]: tmp = d[i] * i for j in range(i, n, i): res[j] += tmp * INVMOD[j] * sgn[(j // i) & 1] % MOD res = fps_exp(res, n) return res def MultisetSum(d: list) -> list: """ prod ( 1 / ( 1 - x^i ) ) ^ d[i] d[w] = 重さ w が d[w] 種類 inf 個ずつある r[w] = 重さが w になるような選び方 """ n = len(d) for i in range(len(INVMOD), n): INVMOD.append(-INVMOD[MOD % i] * (MOD // i) % MOD) res = [0] * n for i in range(1, n): if d[i]: tmp = d[i] * i for j in range(i, n, i): res[j] += tmp * INVMOD[j] % MOD res = fps_exp(res, n) return res fac = [1, 1] finv = [1, 1] def tayler_shift(a: list, c: int) -> list: global fac, finv n = len(a) l = len(fac) fac += [0] * (n - l) finv += [0] * (n - l) for i in range(l, n): fac[i] = fac[i - 1] * i % MOD finv[n - 1] = pow(fac[n - 1], MOD - 2, MOD) for i in reversed(range(l, n - 1)): finv[i] = finv[i + 1] * (i + 1) % MOD r = [x * fac[i] % MOD for i, x in enumerate(a)] b = [0] * n b[0] = 1 for i in range(1, n): b[i] = b[i - 1] * c % MOD * finv[i] % MOD * fac[i - 1] % MOD r.reverse() res = multiply(r, b)[:n] return [x * finv[i] % MOD for i, x in enumerate(reversed(res))] def _fft2d(s: list[list]): h, w = len(s), len(s[0]) for i in range(h): butterfly(s[i]) buf = [0] * h for j in range(w): buf = [s[i][j] for i in range(h)] butterfly(buf) for i in range(h): s[i][j] = buf[i] def _ifft2d(s: list[list]): h, w = len(s), len(s[0]) buf = [0] * h for j in range(w): buf = [s[i][j] for i in range(h)] intt(buf) for i in range(h): s[i][j] = buf[i] for i in range(h): intt(s[i]) def multiply_2D(s: list[list], t: list[list]): """ not verify """ from copy import deepcopy hs, ws = len(s), len(s[0]) ht, wt = len(t), len(t[0]) h = 1 << (hs + ht - 2).bit_length() w = 1 << (ws + wt - 2).bit_length() a = deepcopy(s) b = deepcopy(t) for i in range(hs): a[i] += [0] * (w - ws) for i in range(ht): b[i] += [0] * (w - wt) a += [[0] * w for i in range(h - hs)] b += [[0] * w for i in range(h - ht)] _fft2d(a) _fft2d(b) for i in range(h): for j in range(w): a[i][j] *= b[i][j] a[i][j] %= MOD _ifft2d(a) a = a[: hs + ht - 1] for i in range(hs + ht - 1): a[i][ws + wt - 1 :] = [] return a ・Bostan-Mori法 # https://atcoder.jp/contests/tdpc/submissions/29624177 CONV = FFT(MOD) def Bostan_Mori(P,Q,N): """ [x^n] P(x)/Q(x) を d^2 log N ないし d log d log N で求める """ if N==0: return P[0] Q2=[] for i in range(len(Q)): if i%2:Q2.append(-Q[i]) else:Q2.append(Q[i]) V=CONV.convolution(Q,Q2)[::2] if N%2:return Bostan_Mori(CONV.convolution(P,Q2)[1::2],V,N//2) else:return Bostan_Mori(CONV.convolution(P,Q2)[::2],V,N//2) ・階乗 def factorial(n): F = [1] for i in range(1, n+1): F.append(F[-1]*i%MOD) invF = [pow(F[-1], -1, MOD)] for i in reversed(range(n)): invF.append(invF[-1]*(i+1)%MOD) invF = invF[::-1] return F, invF ・グラフ ランダム生成 def gen_connected_graph(N, M, rng): assert M >= N-1 edges = [] used = set() # まず木を作る for i in range(1, N): p = rng.randint(0, i-1) u, v = min(p, i), max(p, i) edges.append((u, v)) used.add((u, v)) # 残りの辺をランダム追加 while len(edges) < M: u = rng.randint(0, N-1) v = rng.randint(0, N-1) if u == v: continue u, v = min(u, v), max(u, v) if (u, v) in used: continue edges.append((u, v)) used.add((u, v)) return edges ・UnionFind(左右を管理) class UnionFind: def __init__(self, n): self.n = n self.parent_size = [-1]*n self.L = list(range(n)) self.R = list(range(n)) def leader(self, a): if self.parent_size[a] < 0: return a self.parent_size[a] = self.leader(self.parent_size[a]) return self.parent_size[a] def merge(self, a, b): x, y = self.leader(a), self.leader(b) if x == y: return if abs(self.parent_size[x]) < abs(self.parent_size[y]): x, y = y, x self.parent_size[x] += self.parent_size[y] self.parent_size[y] = x self.L[x] = min(self.L[x], self.L[y]) self.R[x] = max(self.R[x], self.R[y]) return def left(self, n): return self.L[self.leader(n)] def right(self, n): return self.R[self.leader(n)] def same(self, a, b): return self.leader(a) == self.leader(b) def size(self, a): return abs(self.parent_size[self.leader(a)]) def groups(self): result = [[] for _ in range(self.n)] for i in range(self.n): result[self.leader(i)].append(i) return [r for r in result if r != []] ・行列式 def det(A): ans = 1 N = len(A) sign = 1 for i in range(N): pivot = i while pivot < N and A[pivot][i] == 0: pivot += 1 if pivot == N: return 0 if pivot != i: A[i], A[pivot] = A[pivot], A[i] sign *= -1 inv = pow(A[i][i], -1, MOD) for j in range(i+1, N): factor = A[j][i]*inv%MOD for k in range(i, N): A[j][k] = (A[j][k]-factor*A[i][k])%MOD ans *= A[i][i] ans %= MOD ans *= sign ans %= MOD return ans ・最小共通祖先(重み付き) class LCA: def __init__(self, G, root=0): V = len(G) self.bit_length = 1 while 1<>1]+1 self.st = [[self.L]*(self.L-(1< 0: ans += self.bit[i] ans %= MOD i -= -i&i return ans def query(self, l, r): if l == 0: return self.sum(r-1) else: return (self.sum(r-1)-self.sum(l-1))%MOD def add(self, i, x): i += 1 while i <= self.size: self.bit[i] += x self.bit[i] %= MOD i += -i&i ・オフラインrangefreq def offline_range_freq_to(A, query): N = len(A) Q = len(query) A = sorted(enumerate(A), key=lambda x:x[1]) query = sorted(enumerate(query), key=lambda x:x[1][2]) F = BIT([0]*N) ans = [-1]*Q idx = 0 for qidx, (l, r, x) in query: while idx < N and A[idx][1] <= x: F.add(A[idx][0], 1) idx += 1 ans[qidx] = F.query(l, r+1) return ans def offline_range_freq_from(A, query): N = len(A) Q = len(query) A = sorted(enumerate(A), key=lambda x:x[1], reverse=True) query = sorted(enumerate(query), key=lambda x:x[1][2], reverse=True) F = BIT([0]*N) ans = [-1]*Q idx = 0 for qidx, (l, r, x) in query: while idx < N and x <= A[idx][1]: F.add(A[idx][0], 1) idx += 1 ans[qidx] = F.query(l, r+1) return ans ・Codonテンプレート #generatorに__getitem__, __contains__を定義 @extend class Generator: def __getitem__(self: Generator[T], _: int) -> T: if self.done(): raise StopIteration() return self.next() def __getitem__(self: Generator[T], key: slice) -> list[T]: assert key.stop is None, ( '''本拡張では、末尾以外でのアンパッキングはできません。 例として、 N, *A = generator には対応しますが *A, N = generator は非対応です。 右辺の generator を list(generator) と書き換えてみてください。''') return list(self) def __contains__(self: Generator[T], key: T) -> bool: return key in list(self) #int同士の除算結果をPythonの負の無限大丸めに合わせる @extend class int: @pure @llvm def _floordiv_int_int(self: int, other: int) -> int: %0 = sdiv i64 %self, %other ret i64 %0 @overload def __floordiv__(self, other: int): d = self._floordiv_int_int(other) m = self - d * other if m and ((other ^ m) < 0): d -= 1 return d @pure @llvm def _mod_int_int(self: int, other: int) -> int: %0 = srem i64 %self, %other ret i64 %0 @overload def __mod__(self, other: int) -> int: m = self._mod_int_int(other) if m and ((other ^ m) < 0): m += other return m #int.bit_length, int.bit_countに対応 @extend class int: def bit_length(self): return 64 - abs(self).__ctlz__() def bit_count(self): return abs(self).__ctpop__() #int hash値をSplitMix64で変更する #Reference: https://prng.di.unimi.it/splitmix64.c @extend class int: def __hash__(self) -> int: z: UInt[64] = UInt[64](self) + UInt[64](0x9e3779b97f4a7c15) z = (z ^ (z >> UInt[64](30))) * UInt[64](0xbf58476d1ce4e5b9) z = (z ^ (z >> UInt[64](27))) * UInt[64](0x94d049bb133111eb) return int(z ^ (z >> UInt[64](31))) #floatの出力桁数を15桁に増やす @extend class float: def __str__(self): return f'{self:15f}' #巨大mod時のオーバーフローを回避 pow(base, -1, mod)に対応 def _extended_pow(): _builtin_pow = pow def _codon_pow(base, exp): return _builtin_pow(base, exp) @overload def _codon_pow(base: int, exp: int, mod: int) -> int: ''' codon用に pow(base, exp, mod) を拡張した関数です。 1. (abs(mod) - 1) ** 2 >= 1 << 63 の場合に発生していたオーバーフローを回避しました。 2. pow(base: int, exp: 負整数, mod: int) による逆元計算に対応しました。 返り値の符号は mod の符号に一致します。 いずれかの引数に INF_MIN := -1 << 63 を渡した場合の動作は未定義です。 ''' if mod == 0: raise ValueError('pow() 3rd argument cannot be 0') if mod == 1 or mod == -1: return 0 if exp < 0: #拡張ユークリッドの互除法 a, b, x, y = base, mod, 1, 0 while b: q = a // b a, b, x, y = b, a - q * b, y, x - q * y if a != 1 and a != -1: raise ValueError('base is not invertible for the given modulus') b128, m128 = Int[128](x), Int[128](mod) if a == -1: b128 = - b128 exp = - exp else: b128, m128 = Int[128](base), Int[128](mod) v128 = Int[128](1) while exp: if exp & 1 == 1: v128 = v128 * b128 % m128 b128 = b128 * b128 % m128 exp >>= 1 v = int(v128) return v + mod if v != 0 and (0 < v) != (0 < mod) else v return _codon_pow pow = _extended_pow() ・スライド最小値(最大値) class MonotonicQueue: def __init__(self, A, isMIN=True): self.N = len(A) self.A = A self.isMIN = isMIN self.que = deque() self.left = 0 self.right = 0 def _cmp(self, a, b): return a >= b if self.isMIN else a <= b def move_right(self): if self.right == self.N: return False while self.que and self._cmp(self.que[-1][0], self.A[self.right]): self.que.pop() self.que.append((self.A[self.right], self.right)) self.right += 1 return True def move_left(self): if self.left == self.right: return False if self.que[0][1] == self.left: self.que.popleft() self.left += 1 return True def get(self): return self.que[0][0] if self.que else None ・オイラーグラフ判定 def isEulerGraph(G, isDirected = False): N = len(G) if not isDirected: cnt = [0]*N UF = UnionFind(N) for n in range(N): for v in G[n]: cnt[n] += 1 UF.merge(n, v) odd = sum(cnt[i]%2 == 1 for i in range(N)) cntE = sum(1 <= cnt[i] for i in range(N)) if odd == 0: if 1 <= cntE: for i in range(N): if 1 <= cnt[i]: if UF.size(i) == cntE: return True, True, i, -1 else: return False, False, -1, -1 else: return True, True, -1, -1 elif odd == 2: l, r = -1, -1 for i in range(N): if cnt[i]%2 == 1: if l == -1: l = i else: r = i break if UF.size(l) == cntE: return True, False, l, r else: return False, False, -1, -1 else: return False, False, -1, -1 else: IN = [0]*N OUT = [0]*N UF = UnionFind(N) for n in range(N): for v in G[n]: OUT[n] += 1 IN[v] += 1 UF.merge(n, v) s, g = -1, -1 m = -1 cntE = sum(1 <= IN[i]+OUT[i] for i in range(N)) for i in range(N): if IN[i]+1 == OUT[i]: if s == -1: s = i else: return False, False, -1, -1 elif OUT[i]+1 == IN[i]: if g == -1: g = i else: return False, False, -1, -1 elif IN[i] != OUT[i]: return False, False, -1, -1 if 1 <= IN[i]+OUT[i] and m == -1: m = i if m != -1 and UF.size(m) != cntE: return False, False, -1, -1 if s == g == -1: return True, True, m, -1 elif s != -1 and g != -1: return True, False, s, g else: return False, False, -1, -1 ・二項係数の区間和 class rangeBinomial: def __init__(self, N, L, R): self.N = N self.L = L self.R = R self.val = 0 for i in range(L, R+1): self.val += cp.C(N, i) self.val %= MOD self.half = pow(2, -1, MOD) def add(self): self.val *= 2 self.val %= MOD self.val += cp.C(self.N, self.L-1)-cp.C(self.N, self.R) self.val %= MOD self.N += 1 return True def sub(self): if self.N == 0: return False self.N -= 1 self.val += cp.C(self.N, self.R)-cp.C(self.N, self.L-1) self.val %= MOD self.val *= self.half self.val %= MOD return True def move_ll(self): if self.L == 0: return False self.L -= 1 self.val += cp.C(self.N, self.L) self.val %= MOD return True def move_lr(self): if self.L == self.R: return False self.val -= cp.C(self.N, self.L) self.val %= MOD self.L += 1 return True def move_rr(self): self.R += 1 self.val += cp.C(self.N, self.R) self.val %= MOD return True def move_rl(self): if self.L == self.R: return False self.val -= cp.C(self.N, self.R) self.val %= MOD self.R -= 1 return True def get(self): return self.val ・円の交点 def circle_intersections(x1, y1, r1, x2, y2, r2): d = distance(x1, y1, x2, y2) dx = x2 - x1 dy = y2 - y1 # 交点なし if d > r1 + r2 or d < abs(r1 - r2) or d == 0: return [] a = (r1*r1 - r2*r2 + d*d) / (2*d) h_sq = r1*r1 - a*a if h_sq < 0: h_sq = 0 h = h_sq**0.5 x3 = x1 + a * dx / d y3 = y1 + a * dy / d rx = -dy * (h / d) ry = dx * (h / d) return [(x3 + rx, y3 + ry), (x3 - rx, y3 - ry)] ・2-SAT class TwoSAT: def __init__(self, N): self.N = N self.edge = [] self.solution = [False]*N self.solved = False def add_constraint(self, u, f, v, g): self.edge.append((u+self.N*f, v+self.N*g)) def add_clause(self, u, f, v, g): self.add_constraint(u, f^True, v, g) self.add_constraint(v, g^True, u, f) def solve(self): SCC = scc(self.N*2, self.edge) order = [-1]*(self.N*2) for i, s in enumerate(SCC): for n in s: order[n] = i for i in range(self.N): if order[i] == order[i+self.N]: return False if order[i] > order[i+self.N]: self.solution[i] = False else: self.solution[i] = True self.solved = True return True def result(self): return self.solution if self.solved else None ・2-SAT(密グラフ) class TwoSAT: def __init__(self, N, M): self.N = N self.M = M self.edge = [] self.solution = [False]*self.N self.cnt = 0 self.solved = False def add_constraint(self, u, f, v, g): self.edge.append((u+(self.N+self.M)*f, v+(self.N+self.M)*g)) def add_clause(self, u, f, v, g): self.add_constraint(u, f^True, v, g) self.add_constraint(v, g^True, u, f) def add_at_most_one(self, A): for i in range(len(A)): self.add_clause(A[i][0], A[i][1], self.N+self.cnt+i, True) if i+1 < len(A): self.add_clause(self.N+self.cnt+i, False, self.N+self.cnt+i+1, True) self.add_clause(self.N+self.cnt+i, False, A[i+1][0], A[i+1][1]) self.cnt += len(A) def solve(self): SCC = scc((self.N+self.M)*2, self.edge) order = [-1]*((self.N+self.M)*2) for i, s in enumerate(SCC): for n in s: order[n] = i for i in range(self.N): if order[i] == order[i+self.N+self.M]: return False if order[i] > order[i+self.N+self.M]: self.solution[i] = False else: self.solution[i] = True self.solved = True return True def result(self): return self.solution[:self.N] if self.solved else None ・Slope-Trick from heapq import heappush, heappop class SlopeTrick: def __init__(self): self.min_f = 0 self.L = [] self.R = [] self.add_l = 0 self.add_r = 0 def push_R(self, a): heappush(self.R, a-self.add_r) def top_R(self): return self.R[0]+self.add_r if self.R else INF def pop_R(self): val = self.top_R() if self.R: heappop(self.R) return val def push_L(self, a): heappush(self.L, -(a-self.add_l)) def top_L(self): return -self.L[0]+self.add_l if self.L else -INF def pop_L(self): val = self.top_L() if self.L: heappop(self.L) return val def size(self): return len(self.L)+len(self.R) def query(self): return self.top_L(), self.top_R(), self.min_f def add_all(self, a): self.min_f += a def add_x_minus_a(self, a): self.min_f += max(self.top_L()-a, 0) self.push_L(a) self.push_R(self.pop_L()) def add_a_minus_x(self, a): self.min_f += max(a-self.top_R(), 0) self.push_R(a) self.push_L(self.pop_R()) def add_abs(self, a): self.add_x_minus_a(a) self.add_a_minus_x(a) def clear_right(self): self.R = [] def clear_left(self): self.L = [] def shift2(self, a, b): self.add_l += a self.add_r += b def shift(self, a): self.shift2(a, a) def get(self, x): ret = self.min_f for l in self.L: n = -l+self.add_l ret += max(n-x, 0) for r in self.R: n = r+self.add_r ret += max(x-n, 0) return ret def merge(self, st): if self.size() < st.size(): self.L, st.L = st.L, self.L self.R, st.R = st.R, self.R self.add_l, st.add_l = st.add_l, self.add_l self.add_r, st.add_r = st.add_r, self.add_r self.min_f, st.min_f = st.min_f, self.min_f while st.R: self.add_x_minus_a(st.pop_R()) while st.L: self.add_a_minus_x(st.pop_L()) self.min_f += st.min_f ・monotone minima # select(i, j, k) AijとAikでAikを選ぶならTrue def monotone_minima(H, W, select): def solve(row): L = len(row) if L == 0: return [] row2 = [row[i] for i in range(L) if i%2 == 1] ans2 = solve(row2) ans = [0]*L for i in range(len(ans2)): ans[i*2+1] = ans2[i] idx = 0 for i in range(0, L, 2): ans[i] = idx end = ans[i+1] if i+1 < L else W-1 while idx != end: idx += 1 if select(row[i], ans[i], idx): ans[i] = idx return ans return solve(list(range(H))) ・最大値(最小値)Top2 class Top2: def __init__(self): self.best1 = (-INF, -1) self.best2 = (-INF, -1) def add(self, val, genre): b1v, b1g = self.best1 b2v, b2g = self.best2 if genre == b1g: if val > b1v: self.best1 = (val, genre) else: if val > b1v: self.best2 = self.best1 self.best1 = (val, genre) elif val > b2v: self.best2 = (val, genre) def get_exclude(self, genre): if self.best1[1] != genre: return self.best1[0] else: return self.best2[0] ・LCA(Sparse Table) class LCA: def __init__(self, G, root=0): self.G = G self.N = len(G) self.A = [] self.IN = [0]*self.N self.depth = [0]*self.N self.dfs(root, -1, 0) self.L = len(self.A) self.log = [0]*(self.L + 1) for i in range(2, self.L+1): self.log[i] = self.log[i >> 1] + 1 self.sparse = [None]*(self.log[self.L] + 1) self.sparse[0] = self.A b = 1 for i in range(self.log[self.L]): self.sparse[i+1] = [p if self.depth[p] <= self.depth[q] else q for p, q in zip(self.sparse[i], self.sparse[i][b:])] b <<= 1 @bootstrap def dfs(self, n, p, d): self.depth[n] = d self.IN[n] = len(self.A) self.A.append(n) for v in self.G[n]: if v == p: continue yield self.dfs(v, n, d+1) self.A.append(n) yield def lca(self, u, v): l = self.IN[u] r = self.IN[v] if l > r: l, r = r, l k = self.log[r-l+1] a = self.sparse[k][l] b = self.sparse[k][r-(1< 20: # return False, "質問回数オーバー" # # x = int(tokens[1]) # # if not (1 <= x <= N): # return False, "質問が範囲外" # # if X <= x: # io.send("Yes") # else: # io.send("No") # # elif len(tokens) == 2 and tokens[0] == "!": # answer = int(tokens[1]) # # if answer == X: # return True, "" # else: # return False, f"答えが違う: {answer} != {X}" # # else: # return False, f"不正な出力: {line}" # ========================================== return True, "" def prepare(filename): """ 実行するコマンドを返す。 Pythonならそのまま。 C++なら先にコンパイルする。 """ if filename.endswith(".py"): return [sys.executable, filename] if filename.endswith(".cpp"): exe = filename[:-4] + ".exe" result = subprocess.run( [ "g++", filename, "-std=c++20", "-O2", "-o", exe, ], text=True, capture_output=True, ) if result.returncode != 0: print(f"{filename} のコンパイルに失敗") print(result.stderr) exit() return [exe] raise ValueError(f"対応していないファイル形式: {filename}") class InteractiveProcess: def __init__(self, command): self.proc = subprocess.Popen( command, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, bufsize=1, ) self.stdout_queue = queue.Queue() self.stderr_lines = [] self.transcript = [] self.stdout_thread = threading.Thread( target=self._read_stdout, daemon=True, ) self.stdout_thread.start() self.stderr_thread = threading.Thread( target=self._read_stderr, daemon=True, ) self.stderr_thread.start() def _read_stdout(self): for line in self.proc.stdout: self.stdout_queue.put(line.rstrip("\r\n")) # EOF self.stdout_queue.put(None) def _read_stderr(self): for line in self.proc.stderr: self.stderr_lines.append(line) def send(self, s): """ ジャッジ → 解答 """ if self.proc.poll() is not None: raise RuntimeError( f"解答プログラムが途中で終了した " f"(returncode={self.proc.returncode})" ) s = str(s) self.transcript.append(("Judge", s)) try: self.proc.stdin.write(s + "\n") self.proc.stdin.flush() except BrokenPipeError: raise RuntimeError("解答プログラムが入力を受け取る前に終了した") def recv(self): """ 解答 → ジャッジ """ try: line = self.stdout_queue.get( timeout=TIMEOUT_SECOND ) except queue.Empty: raise TimeoutError( f"{TIMEOUT_SECOND} 秒以内に応答がなかった" ) if line is None: returncode = self.proc.poll() raise RuntimeError( f"解答プログラムが途中で終了した " f"(returncode={returncode})" ) self.transcript.append(("Solution", line)) return line def finish(self): """ 正解判定後、解答プログラムが正常終了するか確認。 """ if self.proc.stdin and not self.proc.stdin.closed: self.proc.stdin.close() try: self.proc.wait(timeout=TIMEOUT_SECOND) except subprocess.TimeoutExpired: raise TimeoutError( "正解を通知した後もプログラムが終了しなかった" ) if self.proc.returncode != 0: raise RuntimeError( f"解答プログラムが異常終了した " f"(returncode={self.proc.returncode})" ) def stop(self): if self.proc.poll() is None: self.proc.kill() self.proc.wait() def print_transcript(self): print("===== transcript =====") for side, text in self.transcript: print(f"{side:8}: {text}") def print_stderr(self): if self.stderr_lines: print("===== stderr =====") print("".join(self.stderr_lines), end="") sol_command = prepare(SOL) for t in range(TEST_COUNT): case = generate() io = InteractiveProcess(sol_command) try: ok, reason = judge(io, case) if not ok: print("WA") print("===== hidden case =====") print(repr(case)) print("===== reason =====") print(reason) io.print_transcript() io.print_stderr() with open("failed.txt", "w") as f: f.write(repr(case)) io.stop() break io.finish() except TimeoutError as e: print("TLE!") print("===== hidden case =====") print(repr(case)) print("===== reason =====") print(e) io.print_transcript() io.print_stderr() with open("failed.txt", "w") as f: f.write(repr(case)) io.stop() break except RuntimeError as e: print("RE / Protocol Error!") print("===== hidden case =====") print(repr(case)) print("===== reason =====") print(e) io.print_transcript() io.print_stderr() with open("failed.txt", "w") as f: f.write(repr(case)) io.stop() break except Exception as e: print("Judge Error!") print("===== hidden case =====") print(repr(case)) print("===== error =====") print(repr(e)) io.print_transcript() io.print_stderr() io.stop() break finally: io.stop() else: print("All tests passed!") ・等差数列の和 def rangeSUM(l, r, c): return (l+r)*c//2 half = pow(2, -1, MOD) def rangeSUM(l, r, c): return (l+r)%MOD*c%MOD*half%MOD ・等差数列MODカウント def rangeModCount(l, r, c, m, k = 0): if l == r: return c if l%m == k else 0 if c == 1: return -1 if l > r: l, r = r, l if (r-l)%(c-1) != 0: return -1 d = (r-l)//(c-1) GCD = gcd(m, d) if l%GCD != k%GCD: return 0 ng = (k-l)//GCD mg = m//GCD dg = d//GCD f = inverse(ng, dg, mg) if c <= f: return 0 else: return (c-f-1)//mg+1 ・重心分解 size = [-1]*N maxidx = [-1]*N @bootstrap def dfs(n, pre): history.append(n) cnt = 1 MAX = -(10**18) for i, (v, c) in enumerate(G[n]): if v != pre and not used[v]: c = yield dfs(v, n) cnt += c if MAX < c: MAX = c maxidx[n] = i size[n] = cnt yield size[n] @bootstrap def dfs2(n, L): if size[G[n][maxidx[n]][0]] > L//2: yield (yield dfs2(G[n][maxidx[n]][0], L)) yield n parent = [-1]*N used = [False]*N que = deque() que.append((0, -1)) while que: n, p = que.popleft() history = [] dfs(n, -1) center = dfs2(n, len(history)) while history: h = history.pop() size[h] = -1 maxidx[h] = -1 used[center] = True parent[center] = p for v, _ in G[center]: if not used[v]: que.append((v, center)) ・ダブリング class Doubling: def __init__(self, nex, max_k=10**18): self.N = len(nex) self.LOG = max(1, max_k.bit_length()) self.dp = [[-1]*self.N for _ in range(self.LOG)] self.dp[0] = nex[:] for i in range(1, self.LOG): for j in range(self.N): self.dp[i][j] = self.dp[i-1][self.dp[i-1][j]] def jump(self, n, k): for i in range(self.LOG): if 1<