ngng628's Library

This documentation is automatically generated by online-judge-tools/verification-helper

:heavy_check_mark: src/nglib/graph/radix_dijkstra.cr

Depends on

Required by

Verified with

Code

require "../constants.cr"

module NgLib
  # $n$ 頂点 $m$ 辺の重み付きグラフに対して、最短経路を求めます。
  #
  # 経路の復元も可能です。
  #
  # このクラスは辺の重みが非負整数であるときのみ使えます。
  # 辺の重みに非負整数以外を使いたい場合は `nglib/graph/dijkstra` を `require` してください。
  class DijkstraGraph
    record Edge, target : Int32, weight : UInt64

    # 基数ヒープ
    private class RadixHeap64(T)
      @s : Int32
      @last : UInt64
      @bit : Int32
      @vs : Array(Array({UInt64, T}))
      @ms : Array(UInt64)

      def initialize
        @s = 0
        @last = 0_u64
        @bit = sizeof(UInt64) * 8
        @vs = Array.new(@bit + 1) { [] of {UInt64, T} }
        @ms = Array.new(@bit + 1) { -1.to_u64! }
      end

      def empty? : Bool
        @s == 0
      end

      def size : Int32
        s
      end

      @[AlwaysInline]
      def get_bit(x : UInt64) : UInt64
        64_u64 - x.leading_zeros_count
      end

      def push(key : UInt64, val : T) : Nil
        @s += 1
        b = get_bit(key ^ @last)
        @vs[b] << {key, val}
        @ms[b] = Math.min(@ms[b], key)
      end

      def pop : {UInt64, T}
        if @ms[0] == -1.to_u64!
          idx = @ms.index! { |elem| elem != -1.to_u64! }
          @last = @ms[idx]
          @vs[idx].each do |v|
            b = get_bit(v[0] ^ @last)
            @vs[b] << v
            @ms[b] = Math.min(@ms[b], v[0])
          end
          @vs[idx].clear
          @ms[idx] = -1.to_u64!
        end

        @s -= 1
        res = @vs[0].last
        @vs[0].pop
        @ms[0] = -1.to_u64! if @vs[0].empty?

        res
      end
    end

    getter size : Int32
    @graph : Array(Array(Edge))

    # $n$ 頂点 $0$ 辺からなるグラフを作成します。
    #
    # ```
    # graph = Dijkstra.new(n)
    # ```
    def initialize(n : Int)
      @size = n.to_i32
      @graph = Array.new(@size) { Array(Edge).new }
    end

    # 非負整数の重み $w$ の辺 $(u, v)$ を追加します。
    #
    # `directed` が `true` の場合、
    # 有向辺とみなして、$u$ から $v$ への辺のみ生やします。
    #
    # ```
    # graph = Dijkstra.new(n)
    # graph.add_edge(u, v, w)                 # => (u) <---w---> (v)
    # graph.add_edge(u, v, w, directed: true) # => (u) ----w---> (v)
    # ```
    def add_edge(u : Int, v : Int, w : Int, directed : Bool = true)
      @graph[u.to_i32] << Edge.new(v.to_i32, w.to_u64)
      @graph[v.to_i32] << Edge.new(u.to_i32, w.to_u64) unless directed
    end

    # 全点対間の最短経路長を返します。
    #
    # ```
    # dists = graph.shortest_path
    # dists # => [[0, 1, 3], [1, 0, 2], [1, 1, 0]]
    # ```
    def shortest_path
      (0...@size).map { |start| shortest_path(start) }
    end

    # 始点 `start` から各頂点への最短経路長を返します。
    #
    # ```
    # dist = graph.shortest_path(2)
    # dist # => [3, 8, 0, 7, 1]
    # ```
    def shortest_path(start : Int)
      dist = [OO] * @size
      dist[start] = 0_i64
      next_node = RadixHeap64(Int32).new
      next_node.push(0_u64, start.to_i32)

      until next_node.empty?
        d, source = next_node.pop
        next if dist[source] < d
        @graph[source].each do |e|
          next_cost = dist[source] + e.weight
          if next_cost < dist[e.target]
            dist[e.target] = next_cost
            next_node.push(next_cost.to_u64, e.target)
          end
        end
      end

      dist
    end

    # 始点 `start` から終点 `dest` への最短経路長を返します。
    #
    # ```
    # dist = graph.shortest_path(start: 2, dest: 0)
    # dist # => 12
    # ```
    def shortest_path(start : Int, dest : Int)
      shortest_path(start)[dest]
    end

    # 始点 `start` から終点 `dest` への最短経路の一例を返します。
    #
    # ```
    # route = graph.shortest_path_route(start: 2, dest: 0)
    # route # => [2, 7, 1, 0]
    # ```
    def shortest_path_route(start, dest)
      prev = impl_memo_route(start)

      res = Array(Int32).new
      now : Int32? = dest.to_i32
      until now.nil?
        res << now.not_nil!
        now = prev[now]
      end

      res.reverse
    end

    # 始点 `start` から最短路木を構築します。
    #
    # 最短路木は `start` からの最短経路のみを残した全域木です。
    #
    # ```
    # route = graph.shortest_path_route(start: 2, dest: 0)
    # route # => [2, 7, 1, 0]
    # ```
    def shortest_path_tree(start, directed : Bool = true) : Array(Array(Int32))
      dist = [OO] * @size
      dist[start] = 0_i64
      next_node = RadixHeap64(Int32).new
      next_node.push(0_u64, start.to_i32)

      birth = [-1] * @size
      until next_node.empty?
        d, source = next_node.pop
        next if dist[source] < d
        @graph[source].each do |e|
          next_cost = dist[source] + e.weight
          if next_cost < dist[e.target]
            dist[e.target] = next_cost
            next_node.push(next_cost.to_u64, e.target)
            birth[e.target] = source
          end
        end
      end

      tree = Array.new(@size) { [] of Int32 }
      @size.times do |target|
        source = birth[target]
        next if source == -1
        tree[source] << target
        tree[target] << source unless directed
      end

      tree
    end

    # 経路復元のための「どこから移動してきたか」を
    # メモした配列を返します。
    private def impl_memo_route(start)
      dist = [OO] * @size
      dist[start] = 0_i64
      prev = Array(Int32?).new(@size) { nil }
      next_node = RadixHeap64(Int32).new
      next_node.push(0_u64, start.to_i32)

      until next_node.empty?
        d, source = next_node.pop
        next if dist[source] < d
        @graph[source].each do |e|
          next_cost = dist[source] + e.weight
          if next_cost < dist[e.target]
            dist[e.target] = next_cost
            prev[e.target] = source
            next_node.push(next_cost.to_u64, e.target)
          end
        end
      end

      prev
    end
  end
end
# require "../constants.cr"
OO = (1_i64 << 62) - (1_i64 << 31)

module NgLib
  # $n$ 頂点 $m$ 辺の重み付きグラフに対して、最短経路を求めます。
  #
  # 経路の復元も可能です。
  #
  # このクラスは辺の重みが非負整数であるときのみ使えます。
  # 辺の重みに非負整数以外を使いたい場合は `nglib/graph/dijkstra` を `require` してください。
  class DijkstraGraph
    record Edge, target : Int32, weight : UInt64

    # 基数ヒープ
    private class RadixHeap64(T)
      @s : Int32
      @last : UInt64
      @bit : Int32
      @vs : Array(Array({UInt64, T}))
      @ms : Array(UInt64)

      def initialize
        @s = 0
        @last = 0_u64
        @bit = sizeof(UInt64) * 8
        @vs = Array.new(@bit + 1) { [] of {UInt64, T} }
        @ms = Array.new(@bit + 1) { -1.to_u64! }
      end

      def empty? : Bool
        @s == 0
      end

      def size : Int32
        s
      end

      @[AlwaysInline]
      def get_bit(x : UInt64) : UInt64
        64_u64 - x.leading_zeros_count
      end

      def push(key : UInt64, val : T) : Nil
        @s += 1
        b = get_bit(key ^ @last)
        @vs[b] << {key, val}
        @ms[b] = Math.min(@ms[b], key)
      end

      def pop : {UInt64, T}
        if @ms[0] == -1.to_u64!
          idx = @ms.index! { |elem| elem != -1.to_u64! }
          @last = @ms[idx]
          @vs[idx].each do |v|
            b = get_bit(v[0] ^ @last)
            @vs[b] << v
            @ms[b] = Math.min(@ms[b], v[0])
          end
          @vs[idx].clear
          @ms[idx] = -1.to_u64!
        end

        @s -= 1
        res = @vs[0].last
        @vs[0].pop
        @ms[0] = -1.to_u64! if @vs[0].empty?

        res
      end
    end

    getter size : Int32
    @graph : Array(Array(Edge))

    # $n$ 頂点 $0$ 辺からなるグラフを作成します。
    #
    # ```
    # graph = Dijkstra.new(n)
    # ```
    def initialize(n : Int)
      @size = n.to_i32
      @graph = Array.new(@size) { Array(Edge).new }
    end

    # 非負整数の重み $w$ の辺 $(u, v)$ を追加します。
    #
    # `directed` が `true` の場合、
    # 有向辺とみなして、$u$ から $v$ への辺のみ生やします。
    #
    # ```
    # graph = Dijkstra.new(n)
    # graph.add_edge(u, v, w)                 # => (u) <---w---> (v)
    # graph.add_edge(u, v, w, directed: true) # => (u) ----w---> (v)
    # ```
    def add_edge(u : Int, v : Int, w : Int, directed : Bool = true)
      @graph[u.to_i32] << Edge.new(v.to_i32, w.to_u64)
      @graph[v.to_i32] << Edge.new(u.to_i32, w.to_u64) unless directed
    end

    # 全点対間の最短経路長を返します。
    #
    # ```
    # dists = graph.shortest_path
    # dists # => [[0, 1, 3], [1, 0, 2], [1, 1, 0]]
    # ```
    def shortest_path
      (0...@size).map { |start| shortest_path(start) }
    end

    # 始点 `start` から各頂点への最短経路長を返します。
    #
    # ```
    # dist = graph.shortest_path(2)
    # dist # => [3, 8, 0, 7, 1]
    # ```
    def shortest_path(start : Int)
      dist = [OO] * @size
      dist[start] = 0_i64
      next_node = RadixHeap64(Int32).new
      next_node.push(0_u64, start.to_i32)

      until next_node.empty?
        d, source = next_node.pop
        next if dist[source] < d
        @graph[source].each do |e|
          next_cost = dist[source] + e.weight
          if next_cost < dist[e.target]
            dist[e.target] = next_cost
            next_node.push(next_cost.to_u64, e.target)
          end
        end
      end

      dist
    end

    # 始点 `start` から終点 `dest` への最短経路長を返します。
    #
    # ```
    # dist = graph.shortest_path(start: 2, dest: 0)
    # dist # => 12
    # ```
    def shortest_path(start : Int, dest : Int)
      shortest_path(start)[dest]
    end

    # 始点 `start` から終点 `dest` への最短経路の一例を返します。
    #
    # ```
    # route = graph.shortest_path_route(start: 2, dest: 0)
    # route # => [2, 7, 1, 0]
    # ```
    def shortest_path_route(start, dest)
      prev = impl_memo_route(start)

      res = Array(Int32).new
      now : Int32? = dest.to_i32
      until now.nil?
        res << now.not_nil!
        now = prev[now]
      end

      res.reverse
    end

    # 始点 `start` から最短路木を構築します。
    #
    # 最短路木は `start` からの最短経路のみを残した全域木です。
    #
    # ```
    # route = graph.shortest_path_route(start: 2, dest: 0)
    # route # => [2, 7, 1, 0]
    # ```
    def shortest_path_tree(start, directed : Bool = true) : Array(Array(Int32))
      dist = [OO] * @size
      dist[start] = 0_i64
      next_node = RadixHeap64(Int32).new
      next_node.push(0_u64, start.to_i32)

      birth = [-1] * @size
      until next_node.empty?
        d, source = next_node.pop
        next if dist[source] < d
        @graph[source].each do |e|
          next_cost = dist[source] + e.weight
          if next_cost < dist[e.target]
            dist[e.target] = next_cost
            next_node.push(next_cost.to_u64, e.target)
            birth[e.target] = source
          end
        end
      end

      tree = Array.new(@size) { [] of Int32 }
      @size.times do |target|
        source = birth[target]
        next if source == -1
        tree[source] << target
        tree[target] << source unless directed
      end

      tree
    end

    # 経路復元のための「どこから移動してきたか」を
    # メモした配列を返します。
    private def impl_memo_route(start)
      dist = [OO] * @size
      dist[start] = 0_i64
      prev = Array(Int32?).new(@size) { nil }
      next_node = RadixHeap64(Int32).new
      next_node.push(0_u64, start.to_i32)

      until next_node.empty?
        d, source = next_node.pop
        next if dist[source] < d
        @graph[source].each do |e|
          next_cost = dist[source] + e.weight
          if next_cost < dist[e.target]
            dist[e.target] = next_cost
            prev[e.target] = source
            next_node.push(next_cost.to_u64, e.target)
          end
        end
      end

      prev
    end
  end
end
Back to top page