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

:x: src/min_cost_flow.cr

Depends on

Required by

Verified with

Code

# ac-library.cr by hakatashi https://github.com/google/ac-library.cr
#
# Copyright 2023 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

require "./graph.cr"

module AtCoder
  # Implements [atcoder::mcf_graph](https://atcoder.github.io/ac-library/master/document_en/mincostflow.html).
  #
  # ```
  # flow = AtCoder::MinCostFlow.new(5)
  # flow.add_edge(0, 1, 30, 3)
  # flow.add_edge(0, 2, 60, 9)
  # flow.add_edge(1, 2, 40, 5)
  # flow.add_edge(1, 3, 50, 7)
  # flow.add_edge(2, 3, 20, 8)
  # flow.add_edge(2, 4, 50, 6)
  # flow.add_edge(3, 4, 60, 7)
  # flow.flow(0, 4, 70) # => {70, 1080}
  # ```
  class MinCostFlow
    private record EdgeInfo, capacity : Int64, cost : Int64 | Nil do
      def self.zero
        new(Int64::MAX, 0_i64)
      end

      def +(edge : EdgeInfo)
        from_cost = @cost
        to_cost = edge.cost
        if from_cost.nil? || to_cost.nil?
          return self.class.new(0_i64, nil)
        end

        self.class.new(min(@capacity, edge.capacity), from_cost + to_cost)
      end

      def >(edge : EdgeInfo)
        from_dist = dist
        to_dist = edge.dist

        return true if from_dist.nil?
        return false if to_dist.nil?

        from_dist > to_dist
      end

      def dist
        return nil if @capacity == 0
        @cost
      end

      @[AlwaysInline]
      private def min(a, b)
        a > b ? b : a
      end
    end

    # Implements atcoder::mcf_graph g(n).
    def initialize(@size : Int64)
      @graph = AtCoder::DirectedGraph(Nil, EdgeInfo).new(@size)
      @dists = Array(Int64 | Nil).new(@size, 0_i64)
      @edges = [] of {Int64, Int64}
    end

    # Implements atcoder::mcf_graph.add_edge(from, to, capacity, cost).
    def add_edge(from, to, capacity, cost)
      @graph.add_edge(from, to, EdgeInfo.new(capacity.to_i64, cost.to_i64))
      @graph.add_edge(to, from, EdgeInfo.new(0_i64, -cost.to_i64))
      @edges << {from.to_i64, to.to_i64}
    end

    private def increment_edge_cost(from, to, cost_diff)
      edge = @graph.get_edge(from, to)
      @graph.update_edge(from, to, edge + EdgeInfo.new(edge.capacity, cost_diff))
    end

    private def increment_edge_capacity(from, to, capacity_diff)
      edge = @graph.get_edge(from, to)
      @graph.update_edge(from, to, EdgeInfo.new(edge.capacity + capacity_diff, edge.cost))
    end

    # Implements atcoder::mcf_graph.slope(start, target, flow_limit).
    # ameba:disable Metrics/CyclomaticComplexity
    def slope(start, target, flow_limit : Int | Nil = nil)
      raise ArgumentError.new("start and target cannot be the same") if start == target

      flow_points = [] of {Int64, Int64}

      current_cost = 0_i64

      flowed_capacity = 0_i64
      min_cost = 0_i64

      until flowed_capacity == flow_limit
        nodes = @graph.dijkstra(start)

        target_dist = nodes[target][:dist]

        if target_dist.nil? || target_dist.capacity == 0
          break
        end

        capacity = target_dist.capacity

        # Update edge capacities on the path from start to target
        last_node = target
        until last_node == start
          prev_node = nodes[last_node][:prev].not_nil!

          increment_edge_capacity(prev_node, last_node, -capacity)
          increment_edge_capacity(last_node, prev_node, capacity)

          last_node = prev_node
        end

        # Update edge costs
        @edges.each do |from, to|
          from_dist = nodes[from][:dist]
          to_dist = nodes[to][:dist]

          next if from_dist.nil? || to_dist.nil?
          next if from_dist.cost.nil? || to_dist.cost.nil?

          dist = to_dist.cost.not_nil! - from_dist.cost.not_nil!

          increment_edge_cost(from, to, -dist)
          increment_edge_cost(to, from, dist)
        end

        # Update distants
        nodes.each_with_index do |node, i|
          dist = node[:dist]
          if dist.nil? || @dists[i].nil? || dist.not_nil!.cost.nil?
            @dists[i] = nil
          else
            @dists[i] = @dists[i].not_nil! + dist.not_nil!.cost.not_nil!
          end
        end

        new_cost = @dists[target].not_nil!
        if flow_limit.nil?
          new_capacity = capacity
        else
          new_capacity = min(capacity, flow_limit - flowed_capacity)
        end

        if new_cost != current_cost
          if current_cost == 0 && flowed_capacity != 0
            flow_points << {0_i64, 0_i64}
          end
          flow_points << {flowed_capacity, min_cost}
        end

        min_cost += new_cost * new_capacity
        flowed_capacity += new_capacity
        current_cost = new_cost
      end

      flow_points << {flowed_capacity, min_cost}

      flow_points
    end

    # Implements atcoder::mcf_graph.flow(start, target, flow_limit).
    def flow(start, target, flow_limit : Int | Nil = nil)
      flow_points = slope(start, target, flow_limit)
      flow_points.last
    end

    @[AlwaysInline]
    private def min(a, b)
      a > b ? b : a
    end

    delegate :size, to: @graph
  end
end
# ac-library.cr by hakatashi https://github.com/google/ac-library.cr
#
# Copyright 2023 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# require "./graph.cr"
# ac-library.cr by hakatashi https://github.com/google/ac-library.cr
#
# Copyright 2023 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# require "./priority_queue.cr"
# ac-library.cr by hakatashi https://github.com/google/ac-library.cr
#
# Copyright 2023 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

module AtCoder
  # Implements standard priority queue like [std::priority_queue](https://en.cppreference.com/w/cpp/container/priority_queue).
  #
  # ```
  # q = AtCoder::PriorityQueue(Int64).new
  # q << 1_i64
  # q << 3_i64
  # q << 2_i64
  # q.pop # => 3
  # q.pop # => 2
  # q.pop # => 1
  # ```
  class PriorityQueue(T)
    include Enumerable(T)

    getter heap : Array(T)

    # Create a new queue in ascending order of priority.
    def self.max
      self.new { |a, b| a <= b }
    end

    # Create a new queue in ascending order of priority with the elements in *enumerable*.
    def self.max(enumerable : Enumerable(T))
      self.new(enumerable) { |a, b| a <= b }
    end

    # Create a new queue in descending order of priority.
    def self.min
      self.new { |a, b| a >= b }
    end

    # Create a new queue in descending order of priority with the elements in *enumerable*.
    def self.min(enumerable : Enumerable(T))
      self.new(enumerable) { |a, b| a >= b }
    end

    def initialize
      initialize { |a, b| a <= b }
    end

    # Initializes queue with the elements in *enumerable*.
    def self.new(enumerable : Enumerable(T))
      self.new(enumerable) { |a, b| a <= b }
    end

    # Initializes queue with the custom comperator.
    #
    # If the second argument `b` should be popped earlier than
    # the first argument `a`, return `true`. Else, return `false`.
    #
    # ```
    # q = AtCoder::PriorityQueue(Int64).new { |a, b| a >= b }
    # q << 1_i64
    # q << 3_i64
    # q << 2_i64
    # q.pop # => 1
    # q.pop # => 2
    # q.pop # => 3
    # ```
    def initialize(&block : T, T -> Bool)
      @heap = Array(T).new
      @compare_proc = block
    end

    # Initializes queue with the elements in *enumerable* and the custom comperator.
    #
    # If the second argument `b` should be popped earlier than
    # the first argument `a`, return `true`. Else, return `false`.
    #
    # ```
    # q = AtCoder::PriorityQueue.new([1, 3, 2]) { |a, b| a >= b }
    # q.pop # => 1
    # q.pop # => 2
    # q.pop # => 3
    # ```
    def initialize(enumerable : Enumerable(T), &block : T, T -> Bool)
      @heap = enumerable.to_a
      @compare_proc = block

      len = @heap.size
      (len // 2 - 1).downto(0) do |parent|
        v = @heap[parent]
        child = parent * 2 + 1
        while child < len
          if child + 1 < len && @compare_proc.call(@heap[child], @heap[child + 1])
            child += 1
          end
          unless @compare_proc.call(v, @heap[child])
            break
          end
          @heap[parent] = @heap[child]
          parent = child
          child = parent * 2 + 1
        end
        @heap[parent] = v
      end
    end

    # Pushes value into the queue.
    # This method returns self, so several calls can be chained.
    def push(v : T) : self
      @heap << v
      index = @heap.size - 1
      while index != 0
        parent = (index - 1) // 2
        if @compare_proc.call(@heap[index], @heap[parent])
          break
        end
        @heap[parent], @heap[index] = @heap[index], @heap[parent]
        index = parent
      end
      self
    end

    # Alias of `push`
    def <<(v : T) : self
      push(v)
    end

    # Pops value from the queue.
    def pop
      if @heap.size == 0
        return nil
      end
      if @heap.size == 1
        return @heap.pop
      end
      ret = @heap.first
      @heap[0] = @heap.pop
      index = 0
      while index * 2 + 1 < @heap.size
        child = if index * 2 + 2 < @heap.size && !@compare_proc.call(@heap[index * 2 + 2], @heap[index * 2 + 1])
                  index * 2 + 2
                else
                  index * 2 + 1
                end
        if @compare_proc.call(@heap[child], @heap[index])
          break
        end
        @heap[child], @heap[index] = @heap[index], @heap[child]
        index = child
      end
      ret
    end

    # Pops value from the queue.
    # Raises `Enumerable::EmptyError` if queue is of 0 size.
    def pop!
      pop || raise Enumerable::EmptyError.new
    end

    # Yields each item in the queue in comparator's order.
    def each(&)
      @heap.sort { |a, b| @compare_proc.call(a, b) ? 1 : -1 }.each do |e|
        yield e
      end
    end

    # Returns, but does not remove, the head of the queue.
    def first(&)
      @heap.first { yield }
    end

    # Returns `true` if the queue is empty.
    delegate :empty?, to: @heap

    # Returns size of the queue.
    delegate :size, to: @heap
  end
end

module AtCoder
  class Graph(NodeInfo, EdgeInfo)
    @size_bits : Int32
    getter visited : Set(Int64)

    def initialize(@nodes : Array(NodeInfo))
      @size = @nodes.size.to_i64
      @edges = [] of EdgeInfo
      @adjacencies = Array(Hash(Int64, Int64)).new(@size) { Hash(Int64, Int64).new }
      @visited = Set(Int64).new
      @size_bits = @size.to_s(2).size
    end

    def initialize(@size : Int64, initial_node : NodeInfo = nil)
      @nodes = Array(NodeInfo).new(@size, initial_node)
      @edges = [] of EdgeInfo
      @adjacencies = Array(Hash(Int64, Int64)).new(@size) { Hash(Int64, Int64).new }
      @visited = Set(Int64).new
      @size_bits = @size.to_s(2).size
    end

    # Performs Dijkstra's Algorithm to calculate the distance of each node from `start_node`.
    # To use this method, `EdgeInfo` must implement `.zero` and `#+(EdgeInfo)` and `#>(EdgeInfo)`.
    def dijkstra(start_node)
      dist = Array(EdgeInfo | Nil).new(@size, nil)
      dist[start_node] = EdgeInfo.zero

      prev_nodes = Array(Int64 | Nil).new(@size, nil)

      queue = AtCoder::PriorityQueue({EdgeInfo, Int64}).new { |(ad, av), (bd, bv)| ad > bd }
      queue << {EdgeInfo.zero, start_node.to_i64}

      until queue.empty?
        d, v = queue.pop.not_nil!
        current_dist = dist[v].not_nil!
        next if d > current_dist

        @adjacencies[v].each do |(to, edge_id)|
          cost = @edges[edge_id]
          target_dist = dist[to]
          if target_dist.nil? || target_dist > current_dist + cost
            dist[to] = current_dist + cost
            prev_nodes[to] = v
            queue << {current_dist + cost, to}
          end
        end
      end

      Array({dist: EdgeInfo | Nil, prev: Int64 | Nil}).new(@size) do |i|
        {dist: dist[i], prev: prev_nodes[i]}
      end
    end

    def dfs(node : Int64, initial_value : T, &block : Int64, T, NamedTuple(
              node: Int64,
              node_info: NodeInfo | Nil,
              edge: Int64 | Nil,
              edge_info: EdgeInfo | Nil,
              parent: Int64,
            ), (T ->) ->) forall T
      @visited = Set(Int64).new
      info = {
        node:      node,
        node_info: @nodes[node].as(NodeInfo | Nil),
        edge:      nil.as(Int64 | Nil),
        edge_info: nil.as(EdgeInfo | Nil),
        parent:    -1_i64,
      }
      block.call(node, initial_value, info, ->(new_value : T) {
        dfs(node, -1_i64, new_value, &block)
      })
    end

    private def dfs(node : Int64, parent : Int64, value : T, &block : Int64, T, NamedTuple(
                      node: Int64,
                      node_info: NodeInfo | Nil,
                      edge: Int64 | Nil,
                      edge_info: EdgeInfo | Nil,
                      parent: Int64,
                    ), (T ->) ->) forall T
      @visited << node
      @adjacencies[node].each do |(child, edge)|
        next if @visited.includes?(child)
        info = {
          node:      child,
          node_info: @nodes[child].as(NodeInfo | Nil),
          edge:      edge.as(Int64 | Nil),
          edge_info: @edges[edge].as(EdgeInfo | Nil),
          parent:    node,
        }
        block.call(child, value, info, ->(new_value : T) {
          dfs(child, node, new_value, &block)
        })
      end
    end
  end

  class DirectedGraph(NodeInfo, EdgeInfo) < Graph(NodeInfo, EdgeInfo)
    def add_edge(from, to, edge : EdgeInfo = 1_i64)
      @edges << edge
      edge_id = @edges.size.to_i64 - 1
      @adjacencies[from][to.to_i64] = edge_id
      edge_id
    end

    def get_edge(from, to)
      edge_id = @adjacencies[from][to.to_i64]
      @edges[edge_id]
    end

    def update_edge(from, to, edge : EdgeInfo = 1_i64)
      edge_id = @adjacencies[from][to.to_i64]
      @edges[edge_id] = edge
      edge_id
    end
  end

  class UndirectedGraph(NodeInfo, EdgeInfo) < Graph(NodeInfo, EdgeInfo)
    def add_edge(a, b, edge : EdgeInfo = 1_i64)
      @edges << edge
      edge_id = @edges.size.to_i64 - 1
      @adjacencies[a][b.to_i64] = edge_id
      @adjacencies[b][a.to_i64] = edge_id
      edge_id
    end

    def get_edge(a, b)
      edge_id = @adjacencies[a][b.to_i64]
      @edges[edge_id]
    end

    def update_edge(a, b, edge : EdgeInfo = 1_i64)
      edge_id = @adjacencies[a][b.to_i64]
      @edges[edge_id] = edge
      edge_id
    end
  end

  class Tree(NodeInfo, EdgeInfo) < UndirectedGraph(NodeInfo, EdgeInfo)
    @lca_root : Int64 | Nil
    @lca_ancestors : Array(Array(Int64)) | Nil
    @lca_depths : Array(Int64) | Nil

    def diameter
      @farthest_node = -1_i64
      @farthest_depth = 0_i64
      dfs(0_i64, 0_i64) do |node, depth, info, callback|
        weight = info[:edge_info]
        depth += weight.nil? ? 0 : weight
        if @farthest_depth.not_nil! < depth
          @farthest_node = node
          @farthest_depth = depth
        end
        callback.call(depth)
      end

      start_node = @farthest_node.not_nil!
      @farthest_node = -1_i64
      @farthest_depth = 0_i64
      @parents = Array(Int64).new(@size, -1_i64)
      dfs(start_node, 0_i64) do |node, depth, info, callback|
        weight = info[:edge_info]
        depth += weight.nil? ? 0 : weight
        @parents.not_nil![node] = info[:parent]
        if @farthest_depth.not_nil! < depth
          @farthest_node = node
          @farthest_depth = depth
        end
        callback.call(depth)
      end

      {@farthest_depth.not_nil!, start_node, @farthest_node.not_nil!, @parents.not_nil!}
    end

    private def lca_precompute(root)
      lca_ancestors = Array(Array(Int64)).new(@size) { Array(Int64).new(@size_bits, -1_i64) }
      lca_depths = Array(Int64).new(@size, -1_i64)

      dfs(root, 0_i64) do |node, depth, info, callback|
        lca_ancestors[node][0] = info[:parent]
        lca_depths[node] = depth
        callback.call(depth + 1)
      end

      1.upto(@size_bits - 1) do |i|
        @size.times do |node|
          if lca_ancestors[node][i - 1] == -1
            lca_ancestors[node][i] = -1_i64
          else
            lca_ancestors[node][i] = lca_ancestors[lca_ancestors[node][i - 1]][i - 1]
          end
        end
      end

      @lca_root = root
      @lca_ancestors = lca_ancestors
      @lca_depths = lca_depths
    end

    private def lca_nth_prev(node, dist)
      lca_ancestors = @lca_ancestors.not_nil!

      i = 0_i64
      until dist == 0
        if dist.odd?
          node = lca_ancestors[node][i]
          if node == -1
            raise Exception.new("#{dist}th previous node of #{node} does not exist")
          end
        end
        dist //= 2
        i += 1
      end

      node
    end

    # ameba:disable Metrics/CyclomaticComplexity
    def lca(a, b)
      if @lca_root.nil?
        lca_precompute(0_i64)
      end

      lca_ancestors = @lca_ancestors.not_nil!
      lca_depths = @lca_depths.not_nil!

      depth_a = lca_depths[a]
      depth_b = lca_depths[b]

      if depth_a < depth_b
        depth_a, depth_b, a, b = depth_b, depth_a, b, a
      end

      if depth_a != depth_b
        a = lca_nth_prev(a, depth_a - depth_b)
      end

      if a == b
        return a
      end

      (@size_bits - 1).downto(0) do |i|
        if lca_ancestors[a][i] == -1 || lca_ancestors[b][i] == -1
          next
        end

        if lca_ancestors[a][i] != lca_ancestors[b][i]
          a = lca_ancestors[a][i]
          b = lca_ancestors[b][i]
        end
      end

      a = lca_ancestors[a][0]
      b = lca_ancestors[b][0]

      if a != b || a == -1 || b == -1
        raise Exception.new("Assertion error")
      end

      a
    end

    def dist(a, b)
      lca_node = lca(a, b)

      lca_depths = @lca_depths.not_nil!
      (lca_depths[a] - lca_depths[lca_node]) + (lca_depths[b] - lca_depths[lca_node])
    end

    # Returns `true` if node `c` is on the path from `a` to `b`.
    def on_path?(a, b, c)
      dist(a, c) + dist(c, b) == dist(a, b)
    end
  end
end

module AtCoder
  # Implements [atcoder::mcf_graph](https://atcoder.github.io/ac-library/master/document_en/mincostflow.html).
  #
  # ```
  # flow = AtCoder::MinCostFlow.new(5)
  # flow.add_edge(0, 1, 30, 3)
  # flow.add_edge(0, 2, 60, 9)
  # flow.add_edge(1, 2, 40, 5)
  # flow.add_edge(1, 3, 50, 7)
  # flow.add_edge(2, 3, 20, 8)
  # flow.add_edge(2, 4, 50, 6)
  # flow.add_edge(3, 4, 60, 7)
  # flow.flow(0, 4, 70) # => {70, 1080}
  # ```
  class MinCostFlow
    private record EdgeInfo, capacity : Int64, cost : Int64 | Nil do
      def self.zero
        new(Int64::MAX, 0_i64)
      end

      def +(edge : EdgeInfo)
        from_cost = @cost
        to_cost = edge.cost
        if from_cost.nil? || to_cost.nil?
          return self.class.new(0_i64, nil)
        end

        self.class.new(min(@capacity, edge.capacity), from_cost + to_cost)
      end

      def >(edge : EdgeInfo)
        from_dist = dist
        to_dist = edge.dist

        return true if from_dist.nil?
        return false if to_dist.nil?

        from_dist > to_dist
      end

      def dist
        return nil if @capacity == 0
        @cost
      end

      @[AlwaysInline]
      private def min(a, b)
        a > b ? b : a
      end
    end

    # Implements atcoder::mcf_graph g(n).
    def initialize(@size : Int64)
      @graph = AtCoder::DirectedGraph(Nil, EdgeInfo).new(@size)
      @dists = Array(Int64 | Nil).new(@size, 0_i64)
      @edges = [] of {Int64, Int64}
    end

    # Implements atcoder::mcf_graph.add_edge(from, to, capacity, cost).
    def add_edge(from, to, capacity, cost)
      @graph.add_edge(from, to, EdgeInfo.new(capacity.to_i64, cost.to_i64))
      @graph.add_edge(to, from, EdgeInfo.new(0_i64, -cost.to_i64))
      @edges << {from.to_i64, to.to_i64}
    end

    private def increment_edge_cost(from, to, cost_diff)
      edge = @graph.get_edge(from, to)
      @graph.update_edge(from, to, edge + EdgeInfo.new(edge.capacity, cost_diff))
    end

    private def increment_edge_capacity(from, to, capacity_diff)
      edge = @graph.get_edge(from, to)
      @graph.update_edge(from, to, EdgeInfo.new(edge.capacity + capacity_diff, edge.cost))
    end

    # Implements atcoder::mcf_graph.slope(start, target, flow_limit).
    # ameba:disable Metrics/CyclomaticComplexity
    def slope(start, target, flow_limit : Int | Nil = nil)
      raise ArgumentError.new("start and target cannot be the same") if start == target

      flow_points = [] of {Int64, Int64}

      current_cost = 0_i64

      flowed_capacity = 0_i64
      min_cost = 0_i64

      until flowed_capacity == flow_limit
        nodes = @graph.dijkstra(start)

        target_dist = nodes[target][:dist]

        if target_dist.nil? || target_dist.capacity == 0
          break
        end

        capacity = target_dist.capacity

        # Update edge capacities on the path from start to target
        last_node = target
        until last_node == start
          prev_node = nodes[last_node][:prev].not_nil!

          increment_edge_capacity(prev_node, last_node, -capacity)
          increment_edge_capacity(last_node, prev_node, capacity)

          last_node = prev_node
        end

        # Update edge costs
        @edges.each do |from, to|
          from_dist = nodes[from][:dist]
          to_dist = nodes[to][:dist]

          next if from_dist.nil? || to_dist.nil?
          next if from_dist.cost.nil? || to_dist.cost.nil?

          dist = to_dist.cost.not_nil! - from_dist.cost.not_nil!

          increment_edge_cost(from, to, -dist)
          increment_edge_cost(to, from, dist)
        end

        # Update distants
        nodes.each_with_index do |node, i|
          dist = node[:dist]
          if dist.nil? || @dists[i].nil? || dist.not_nil!.cost.nil?
            @dists[i] = nil
          else
            @dists[i] = @dists[i].not_nil! + dist.not_nil!.cost.not_nil!
          end
        end

        new_cost = @dists[target].not_nil!
        if flow_limit.nil?
          new_capacity = capacity
        else
          new_capacity = min(capacity, flow_limit - flowed_capacity)
        end

        if new_cost != current_cost
          if current_cost == 0 && flowed_capacity != 0
            flow_points << {0_i64, 0_i64}
          end
          flow_points << {flowed_capacity, min_cost}
        end

        min_cost += new_cost * new_capacity
        flowed_capacity += new_capacity
        current_cost = new_cost
      end

      flow_points << {flowed_capacity, min_cost}

      flow_points
    end

    # Implements atcoder::mcf_graph.flow(start, target, flow_limit).
    def flow(start, target, flow_limit : Int | Nil = nil)
      flow_points = slope(start, target, flow_limit)
      flow_points.last
    end

    @[AlwaysInline]
    private def min(a, b)
      a > b ? b : a
    end

    delegate :size, to: @graph
  end
end
Back to top page