require "../data_structure/sparse_table.cr" module NgLib class EulerTourTree @size : Int32 @graph : Array(Array(Int32)) @time : Int32 getter itinerary : Array(Int32) getter login : Array(Int32) getter logout : Array(Int32) getter parents : Array(Int32) getter depths : Array(Int32) @min_depth : SparseTable(Int32) # n 頂点 0 辺のグラフを生成します。 # # ``` # tree = EulerTourTree.new(n) # ``` def initialize(n : Int) @size = n.to_i32 @graph = Array.new(n) { [] of Int32 } @time = 0 @itinerary = [] of Int32 @login = [-1] * n @logout = [-1] * n @parents = [-1] * n @depths = [] of Int32 @min_depth = uninitialized SparseTable(Int32) end # 無向辺 (u, v) を追加します。 # # ``` # tree = EulerTourTree.new(n) # tree.add_edge(u, v) # ``` def add_edge(u : Int, v : Int) @graph[u] << v.to_i32 @graph[v] << u.to_i32 end # 実際にオイラーツアーします。 # # itinerary などが正しく構築されます。 # # ``` # tree = EulerTourTree.new(n) # tree.build # tree.build(root: 3) # ``` def build(root : Int = 0) dfs(root.to_i32, -1, 0) @min_depth = SparseTable(Int32).min(@depths) end # 頂点 u と頂点 v の最小共通祖先を返します。 # # ``` # tree = EulerTourTree.new(n) # tree.build # tree.lca(u, v) # ``` def lca(u : Int, v : Int) l = {@login[u], @logout[u], @login[v], @logout[v]}.min r = {@login[u], @logout[u], @login[v], @logout[v]}.max mn = @min_depth[l...r] ok = r ng = l while ok - ng > 1 mid = (ok + ng) // 2 if @min_depth[l...mid] == mn ok = mid else ng = mid end end @itinerary[ok - 1] end # 根を x とする部分木の頂点のコストの総和を求めるためのリストを返します。 # # `zero` には「総和」の単位元を渡してください。 # # リストの i 番目には時刻 i に訪れた頂点のコストが格納されています。 # ただし、その頂点が時刻 i 以前に訪問済みだった場合は zero が格納されます。 # # 頂点 v のコストが w 変更される場合は、 # このリストの @login[v] 番目を w にすると良いです。 # # ``` # tree = EulerTourTree.new(n) # tree.build # node_cost = Array.new(n) # tree.subtree_node_costs { |v| node_costs[v] } # ``` def subtree_node_costs(zero = U.zero, & : Int32 -> U) forall U costs = Array(U).new(itinerary.size) { zero } @size.times do |v| t = @login[v] cost = yield v costs[t] = cost end end # 根を x とする部分木の辺のコストの総和を求めるためのリストを返します。 # # `zero` には「総和」の単位元を渡してください。 # # リストの i 番目には時刻 i に訪れた頂点と、 # 時刻 i - 1 に訪れた頂点を結ぶ辺のコストが格納されています。 # ただし、その辺が時刻 i 以前に訪問済みだった場合は zero が格納されます # # 無向辺 (u, v) のコストが w に変更される場合は、 # 頂点 u, v のうち、**後に**訪れる方の頂点を bwd として、 # このリストの @login[bwd] 番目を w にすると良いです。 # # ``` # tree = EulerTourTree.new(n) # tree.build # edge_cost = Hash({Int32, Int32}, Int64).new # tree.subtree_edge_costs { |u, v| edge_cost[{u, v}] } # ``` def subtree_edge_costs(zero = U.zero, & : Int32, Int32 -> U) forall U costs = Array(U).new(itinerary.size) { zero } @size.times do |v| t = @login[v] next if t == 0 par = @parents[v] costs[t] = yield par, v end end # 根からのパスに現れる頂点のコストの総和を求めるためのリストを返します。 # # リストの i 番目には時刻 i に訪れた頂点のコストが格納されています。 # ただし、その頂点が時刻 i 以前に訪問済みだった場合は、マイナス倍された値が格納されます。 # # 頂点 v のコストが w 変更される場合は、 # このリストの @login[v] 番目を w に、それ以降で v が現れる時刻番目を -w にすると良いです。 # (つまり、計算量がそこそこかかりそう?) # # ``` # tree = EulerTourTree.new(n) # tree.build # tree.node_costs_on_root_path { |v| node_costs[v] } # ``` def node_costs_on_root_path(& : Int32 -> U) forall U root = itinerary[0] costs = Array(U).new(itinerary.size + 1) costs << yield root (itinerary.size - 1).times do |i| s = itinerary[i] t = itinerary[i + 1] if @parents[t] == s costs << yield t else costs << -(yield s) end end costs << -(yield root) end # 根からのパスに現れる辺のコストの総和を求めるためのリストを返します。 # # `zero` には「総和」の単位元を渡してください。 # # リストの i 番目には時刻 i に訪れた頂点と、 # 時刻 i - 1 に訪れた頂点を結ぶ辺のコストが格納されています。 # ただし、その辺が時刻 i 以前に訪問済みだった場合は、マイナス倍された値が格納されます。 # また、時刻 0 と時刻 itinerary.size には zero が格納されます。 # # 辺 (u, v) のコストが w 変更される場合は、 # 頂点 u, v のうち、**後に**訪れる方の頂点 bwd をとして、 # このリストの @login[bwd] 番目を w に、@logout[bwd] 番目を -w にしてください。 # # ``` # tree = EulerTourTree.new(n) # tree.build # tree.edge_costs_on_root_path { |u, v| edge_cost[{u, v}] } # ``` def edge_costs_on_root_path(zero = U.zero, & : Int32, Int32 -> U) forall U costs = Array(U).new(itinerary.size + 1) costs << U.zero (itinerary.size - 1).times do |i| s = itinerary[i] t = itinerary[i + 1] if @parents[t] == s costs << yield s, t else costs << -(yield s, t) end end costs << U.zero end # 根を subroot とする部分木の頂点のコストの総和を返します。 # # ブロックでは整数 l, r が与えられるので、 # subtree_node_costs の [l, r) までの総和を返してください。 # # ``` # tree = EulerTourTree.new(n) # tree.build # node_cost = Array.new(n) # costs = tree.subtree_node_costs { |v| node_costs[v] } # csum = StaticRangeSum.new(costs) # tree.sum_subtree_node_cost(subroot: 3) { |l, r| csum[l...r] } # ``` def sum_subtree_node_cost(subroot : Int, & : Int32, Int32 -> U) forall U l = @login[subroot] r = @logout[subroot] yield l, r end # 根を subroot とする部分木の辺のコストの総和を返します。 # # ブロックでは整数 l, r が与えられるので、 # subtree_edge_costs の [l, r) までの総和を返してください。 # # ``` # tree = EulerTourTree.new(n) # tree.build # edge_cost = Hash({Int32, Int32}, Int64).new # costs = tree.subtree_node_costs { |u, v| edge_cost[{u, v}] } # csum = StaticRangeSum.new(costs) # tree.sum_subtree_edge_cost(subroot: 3) { |l, r| csum[l...r] } # ``` def sum_subtree_edge_cost(subroot : Int, & : Int32, Int32 -> U) forall U l = @login[subroot] r = @logout[subroot] yield l + 1, r end # 根から頂点 v へのパスに現れる頂点のコストの総和を返します。 # # ブロックでは整数 l, r が与えられるので、 # root_path_node_costs の [l, r) までの総和を返してください。 # # ``` # tree = EulerTourTree.new(n) # tree.build # costs = tree.root_path_node_costs { |v| node_costs[v] } # csum = StaticRangeSum.new(costs) # tree.sum_node_cost_on_path_to(v: 3) { |l, r| csum[l...r] } # ``` def sum_node_cost_on_path_to(v : Int, & : Int32, Int32 -> U) forall U r = @login[v] yield 0, r + 1 end # 根から頂点 v へのパスに現れる辺のコストの総和を返します。 # # ブロックでは整数 l, r が与えられるので、 # root_path_edge_costs の [l, r) までの総和を返してください。 # # ``` # tree = EulerTourTree.new(n) # tree.build # costs = tree.root_path_node_costs { |v| node_costs[v] } # csum = StaticRangeSum.new(costs) # tree.sum_edge_cost_on_path_to(v: 3) { |l, r| csum[l...r] } # ``` def sum_edge_cost_on_path_to(v : Int, & : Int32, Int32 -> U) forall U r = @login[v] yield 1, r + 1 end private def dfs(v : Int32, par : Int32, d : Int32) @itinerary << v @depths << d @parents[v] = par @login[v] = @time @time += 1 @graph[v].each do |child| next if child == par dfs(child, v, d + 1) @itinerary << v @depths << d end @logout[v] = @time @time += 1 end end end
# require "../data_structure/sparse_table.cr" module NgLib # 不変な数列 $A$ について、以下の条件を満たす演算を、区間クエリとして処理します。 # - 結合則 : $(x \oplus y) \oplus z = x \oplus (y \oplus z)$ # - 冪等性 : $x \oplus x = x$ # # 前計算は $O(N \log{N})$ かかりますが、区間クエリには $O(1)$ で答えられます。 class SparseTable(T) getter size : Int32 @data : Array(T) @table : Array(Array(T)) @lookup : Array(Int32) @op : (T, T) -> T # $\oplus = \max$ としてデータ構造を構築します。 def self.max(elems : Enumerable(T)) new elems, ->(x : T, y : T) { x > y ? x : y } end # $\oplus = \min$ としてデータ構造を構築します。 def self.min(elems : Enumerable(T)) new elems, ->(x : T, y : T) { x < y ? x : y } end # $\oplus = \mathrm{bitwise-or}$ としてデータ構造を構築します。 def self.bitwise_or(elems : Enumerable(T)) new elems, ->(x : T, y : T) { x | y } end # $\oplus = \mathrm{bitwise-and}$ としてデータ構造を構築します。 def self.bitwise_and(elems : Enumerable(T)) new elems, ->(x : T, y : T) { x & y } end # $\oplus = \mathrm{gcd}$ としてデータ構造を構築します。 def self.gcd(elems : Enumerable(T)) new elems, ->(x : T, y : T) { x.gcd(y) } end # $\oplus = op$ としてデータ構造を構築します。 def initialize(elems : Enumerable(T), @op : (T, T) -> T) @size = elems.size @data = Array(T).new log = (0..).index! { |k| (1 << k) > @size } @table = Array.new(log) { Array(T).new(1 << log, T.zero) } elems.each_with_index { |e, i| @table[0][i] = e; @data << e } (1...log).each do |i| j = 0 while j + (1 << i) <= (1 << log) @table[i][j] = @op.call(@table[i - 1][j], @table[i - 1][j + (1 << (i - 1))]) j += 1 end end @lookup = [0] * (@size + 1) (2..@size).each do |i| @lookup[i] = @lookup[i >> 1] + 1 end end # `range` の表す範囲の要素の総積 $\bigoplus_{i \in range} a_i$ を返します。 # # ``` # rmq = SparseTable(Int32).min([2, 7, 1, 8, 1]) # rmq.prod(0...3) # => 1 # ``` def prod(range : Range(Int?, Int?)) l = (range.begin || 0) r = if range.end.nil? @size else range.end.not_nil! + (range.exclusive? ? 0 : 1) end b = @lookup[r - l] @op.call(@table[b][l], @table[b][r - (1 << b)]) end # `range` の表す範囲の要素の総積 $\bigoplus_{i \in range} a_i$ を返します。 # # $0 \leq l \leq r \leq n$ を満たさないとき、`nil` を返します。 # # ``` # rmq = SparseTable(Int32).min([2, 7, 1, 8, 1]) # rmq.prod(0...3) # => 1 # ``` def prod?(range : Range(Int?, Int?)) l = (range.begin || 0) r = if range.end.nil? @size else range.end.not_nil! + (range.exclusive? ? 0 : 1) end return nil unless 0 <= l && l <= r && r <= @size prod(range) end # $a_i$ を返します。 def [](i) @data[i] end # $a_i$ を返します。 # # 添字が範囲外のとき、`nil` を返します。 def []?(i) @data[i]? end # `prod` へのエイリアスです。 def [](range : Range(Int?, Int?)) prod(range) end # `prod?` へのエイリアスです。 def []?(range : Range(Int?, Int?)) prod?(range) end end end module NgLib class EulerTourTree @size : Int32 @graph : Array(Array(Int32)) @time : Int32 getter itinerary : Array(Int32) getter login : Array(Int32) getter logout : Array(Int32) getter parents : Array(Int32) getter depths : Array(Int32) @min_depth : SparseTable(Int32) # n 頂点 0 辺のグラフを生成します。 # # ``` # tree = EulerTourTree.new(n) # ``` def initialize(n : Int) @size = n.to_i32 @graph = Array.new(n) { [] of Int32 } @time = 0 @itinerary = [] of Int32 @login = [-1] * n @logout = [-1] * n @parents = [-1] * n @depths = [] of Int32 @min_depth = uninitialized SparseTable(Int32) end # 無向辺 (u, v) を追加します。 # # ``` # tree = EulerTourTree.new(n) # tree.add_edge(u, v) # ``` def add_edge(u : Int, v : Int) @graph[u] << v.to_i32 @graph[v] << u.to_i32 end # 実際にオイラーツアーします。 # # itinerary などが正しく構築されます。 # # ``` # tree = EulerTourTree.new(n) # tree.build # tree.build(root: 3) # ``` def build(root : Int = 0) dfs(root.to_i32, -1, 0) @min_depth = SparseTable(Int32).min(@depths) end # 頂点 u と頂点 v の最小共通祖先を返します。 # # ``` # tree = EulerTourTree.new(n) # tree.build # tree.lca(u, v) # ``` def lca(u : Int, v : Int) l = {@login[u], @logout[u], @login[v], @logout[v]}.min r = {@login[u], @logout[u], @login[v], @logout[v]}.max mn = @min_depth[l...r] ok = r ng = l while ok - ng > 1 mid = (ok + ng) // 2 if @min_depth[l...mid] == mn ok = mid else ng = mid end end @itinerary[ok - 1] end # 根を x とする部分木の頂点のコストの総和を求めるためのリストを返します。 # # `zero` には「総和」の単位元を渡してください。 # # リストの i 番目には時刻 i に訪れた頂点のコストが格納されています。 # ただし、その頂点が時刻 i 以前に訪問済みだった場合は zero が格納されます。 # # 頂点 v のコストが w 変更される場合は、 # このリストの @login[v] 番目を w にすると良いです。 # # ``` # tree = EulerTourTree.new(n) # tree.build # node_cost = Array.new(n) # tree.subtree_node_costs { |v| node_costs[v] } # ``` def subtree_node_costs(zero = U.zero, & : Int32 -> U) forall U costs = Array(U).new(itinerary.size) { zero } @size.times do |v| t = @login[v] cost = yield v costs[t] = cost end end # 根を x とする部分木の辺のコストの総和を求めるためのリストを返します。 # # `zero` には「総和」の単位元を渡してください。 # # リストの i 番目には時刻 i に訪れた頂点と、 # 時刻 i - 1 に訪れた頂点を結ぶ辺のコストが格納されています。 # ただし、その辺が時刻 i 以前に訪問済みだった場合は zero が格納されます # # 無向辺 (u, v) のコストが w に変更される場合は、 # 頂点 u, v のうち、**後に**訪れる方の頂点を bwd として、 # このリストの @login[bwd] 番目を w にすると良いです。 # # ``` # tree = EulerTourTree.new(n) # tree.build # edge_cost = Hash({Int32, Int32}, Int64).new # tree.subtree_edge_costs { |u, v| edge_cost[{u, v}] } # ``` def subtree_edge_costs(zero = U.zero, & : Int32, Int32 -> U) forall U costs = Array(U).new(itinerary.size) { zero } @size.times do |v| t = @login[v] next if t == 0 par = @parents[v] costs[t] = yield par, v end end # 根からのパスに現れる頂点のコストの総和を求めるためのリストを返します。 # # リストの i 番目には時刻 i に訪れた頂点のコストが格納されています。 # ただし、その頂点が時刻 i 以前に訪問済みだった場合は、マイナス倍された値が格納されます。 # # 頂点 v のコストが w 変更される場合は、 # このリストの @login[v] 番目を w に、それ以降で v が現れる時刻番目を -w にすると良いです。 # (つまり、計算量がそこそこかかりそう?) # # ``` # tree = EulerTourTree.new(n) # tree.build # tree.node_costs_on_root_path { |v| node_costs[v] } # ``` def node_costs_on_root_path(& : Int32 -> U) forall U root = itinerary[0] costs = Array(U).new(itinerary.size + 1) costs << yield root (itinerary.size - 1).times do |i| s = itinerary[i] t = itinerary[i + 1] if @parents[t] == s costs << yield t else costs << -(yield s) end end costs << -(yield root) end # 根からのパスに現れる辺のコストの総和を求めるためのリストを返します。 # # `zero` には「総和」の単位元を渡してください。 # # リストの i 番目には時刻 i に訪れた頂点と、 # 時刻 i - 1 に訪れた頂点を結ぶ辺のコストが格納されています。 # ただし、その辺が時刻 i 以前に訪問済みだった場合は、マイナス倍された値が格納されます。 # また、時刻 0 と時刻 itinerary.size には zero が格納されます。 # # 辺 (u, v) のコストが w 変更される場合は、 # 頂点 u, v のうち、**後に**訪れる方の頂点 bwd をとして、 # このリストの @login[bwd] 番目を w に、@logout[bwd] 番目を -w にしてください。 # # ``` # tree = EulerTourTree.new(n) # tree.build # tree.edge_costs_on_root_path { |u, v| edge_cost[{u, v}] } # ``` def edge_costs_on_root_path(zero = U.zero, & : Int32, Int32 -> U) forall U costs = Array(U).new(itinerary.size + 1) costs << U.zero (itinerary.size - 1).times do |i| s = itinerary[i] t = itinerary[i + 1] if @parents[t] == s costs << yield s, t else costs << -(yield s, t) end end costs << U.zero end # 根を subroot とする部分木の頂点のコストの総和を返します。 # # ブロックでは整数 l, r が与えられるので、 # subtree_node_costs の [l, r) までの総和を返してください。 # # ``` # tree = EulerTourTree.new(n) # tree.build # node_cost = Array.new(n) # costs = tree.subtree_node_costs { |v| node_costs[v] } # csum = StaticRangeSum.new(costs) # tree.sum_subtree_node_cost(subroot: 3) { |l, r| csum[l...r] } # ``` def sum_subtree_node_cost(subroot : Int, & : Int32, Int32 -> U) forall U l = @login[subroot] r = @logout[subroot] yield l, r end # 根を subroot とする部分木の辺のコストの総和を返します。 # # ブロックでは整数 l, r が与えられるので、 # subtree_edge_costs の [l, r) までの総和を返してください。 # # ``` # tree = EulerTourTree.new(n) # tree.build # edge_cost = Hash({Int32, Int32}, Int64).new # costs = tree.subtree_node_costs { |u, v| edge_cost[{u, v}] } # csum = StaticRangeSum.new(costs) # tree.sum_subtree_edge_cost(subroot: 3) { |l, r| csum[l...r] } # ``` def sum_subtree_edge_cost(subroot : Int, & : Int32, Int32 -> U) forall U l = @login[subroot] r = @logout[subroot] yield l + 1, r end # 根から頂点 v へのパスに現れる頂点のコストの総和を返します。 # # ブロックでは整数 l, r が与えられるので、 # root_path_node_costs の [l, r) までの総和を返してください。 # # ``` # tree = EulerTourTree.new(n) # tree.build # costs = tree.root_path_node_costs { |v| node_costs[v] } # csum = StaticRangeSum.new(costs) # tree.sum_node_cost_on_path_to(v: 3) { |l, r| csum[l...r] } # ``` def sum_node_cost_on_path_to(v : Int, & : Int32, Int32 -> U) forall U r = @login[v] yield 0, r + 1 end # 根から頂点 v へのパスに現れる辺のコストの総和を返します。 # # ブロックでは整数 l, r が与えられるので、 # root_path_edge_costs の [l, r) までの総和を返してください。 # # ``` # tree = EulerTourTree.new(n) # tree.build # costs = tree.root_path_node_costs { |v| node_costs[v] } # csum = StaticRangeSum.new(costs) # tree.sum_edge_cost_on_path_to(v: 3) { |l, r| csum[l...r] } # ``` def sum_edge_cost_on_path_to(v : Int, & : Int32, Int32 -> U) forall U r = @login[v] yield 1, r + 1 end private def dfs(v : Int32, par : Int32, d : Int32) @itinerary << v @depths << d @parents[v] = par @login[v] = @time @time += 1 @graph[v].each do |child| next if child == par dfs(child, v, d + 1) @itinerary << v @depths << d end @logout[v] = @time @time += 1 end end end