競技プログラミング用 C++ ライブラリ
components/min_cost_circulation.hpp · GitHub で見る
共通ページ
最小費用流 の内部部品です。容量を上位 bit から復元し、負の reduced cost の辺を飽和させてから余剰流量を最短路で解消します。
| 操作 | 内容・返り値 | 計算量 |
|---|---|---|
flow_detail::scaling_flow<Cap,Cost,Total>(graph,costs,s,t,limit) |
残余グラフに追加した流量と費用を pair<Cap,Total> で返す |
\(O(M(N+M\log N)\log U)\) |
graph.build() を済ませ、残余辺ごとの単位費用を costs に入れます。正辺・逆辺の費用は互いに符号反転した値です。公開ライブラリと同じ前提条件で使います。最短路と補助辺の費用は 128 bit、ヒープは 4 分木です。
\(N\) 頂点・\(M\) 辺、容量・流量上限の最大値を \(U\geq1\) として、\(O(M(N+M\log N)\log U)\) 時間、\(O(N+M)\) 空間です。
#ifndef MIN_COST_CIRCULATION_HPP
#define MIN_COST_CIRCULATION_HPP
#include "flow_graph.hpp"
#include "d_ary_heap.hpp"
#include <bit>
#include <cstdint>
#include <functional>
#include <type_traits>
#include <utility>
namespace flow_detail {
template<class Cap, class Cost, class Total>
std::pair<Cap, Total> scaling_flow(Graph<Cap>& graph, const std::vector<Cost>& costs, int s, int t, Cap limit) {
using Wide = __int128_t;
using State = std::pair<Wide, int>;
using Unsigned = std::make_unsigned_t<Cap>;
Cap upper = 0;
for (int j = graph.start[s]; j < graph.start[s + 1]; ++j) upper += std::min<Cap>(limit - upper, graph.arcs[graph.order[j]].cap);
limit = upper;
if (!limit) return {Cap(0), Total(0)};
Graph<Cap> work = graph;
std::vector<Cap> capacity;
capacity.reserve(work.arcs.size() + 2);
Unsigned maximum = 0;
for (auto& e : work.arcs) capacity.push_back(e.cap), maximum = std::max(maximum, Unsigned(e.cap)), e.cap = 0;
std::vector<Wide> cost(costs.begin(), costs.end());
Wide largest = 0;
for (Cost c : costs) largest = std::max(largest, c < 0 ? -Wide(c) : Wide(c));
work.add_edge(t, s, 0);
capacity.push_back(limit), capacity.push_back(0);
cost.push_back(-Wide(graph.n) * largest - 1), cost.push_back(-cost.back());
maximum = std::max(maximum, Unsigned(limit));
work.build();
int n = graph.n;
std::vector<Wide> potential(n), distance(n);
std::vector<std::int64_t> excess(n);
std::vector<int> parent(n), settled, zero;
std::vector<unsigned char> visited(n);
DaryHeap<State, std::greater<State>, 4> heap;
heap.reserve(work.arcs.size());
settled.reserve(n), zero.reserve(n);
const Wide inf = std::numeric_limits<Wide>::max();
for (int bit = int(std::bit_width(maximum)); bit--;) {
for (std::size_t i = 0; i < capacity.size(); ++i) work.arcs[i].cap = work.arcs[i].cap * 2 + ((Unsigned(capacity[i]) >> bit) & 1);
std::fill(excess.begin(), excess.end(), 0);
for (int i = 0; i < int(work.arcs.size()); ++i) {
auto& e = work.arcs[i];
int from = work.arcs[i ^ 1].to;
if (e.cap && cost[i] + potential[from] - potential[e.to] < 0) {
auto amount = e.cap;
assert(amount == 1);
e.cap = 0, work.arcs[i ^ 1].cap += amount;
--excess[from], ++excess[e.to];
}
}
for (int source = 0; source < n; ++source) while (excess[source] > 0) {
std::fill(distance.begin(), distance.end(), inf);
std::fill(visited.begin(), visited.end(), 0);
heap.clear(), settled.clear(), zero.clear();
distance[source] = 0;
zero.push_back(source);
int target = -1;
while (!zero.empty() || !heap.empty()) {
int v;
if (!zero.empty()) v = zero.back(), zero.pop_back();
else {
auto [d, u] = heap.pop_value();
if (distance[u] != d) continue;
v = u;
}
if (visited[v]) continue;
visited[v] = 1, settled.push_back(v);
if (excess[v] < 0) { target = v; break; }
for (int j = work.start[v]; j < work.start[v + 1]; ++j) {
int i = work.order[j];
const auto& e = work.arcs[i];
if (!e.cap || visited[e.to]) continue;
Wide reduced = cost[i] + potential[v] - potential[e.to];
assert(reduced >= 0);
Wide next = distance[v] + reduced;
if (next >= distance[e.to]) continue;
distance[e.to] = next, parent[e.to] = i;
if (!reduced) zero.push_back(e.to);
else heap.push({next, e.to});
}
}
assert(target >= 0);
for (int v : settled) potential[v] -= distance[target] - distance[v];
std::int64_t amount = std::min(excess[source], -excess[target]);
for (int v = target; v != source; v = work.arcs[parent[v] ^ 1].to) {
auto cap = work.arcs[parent[v]].cap;
if (Unsigned(cap) < std::uint64_t(amount)) amount = std::int64_t(cap);
}
for (int v = target; v != source; v = work.arcs[parent[v] ^ 1].to) {
int i = parent[v];
work.arcs[i].cap -= Cap(amount), work.arcs[i ^ 1].cap += Cap(amount);
}
excess[source] -= amount, excess[target] += amount;
}
}
Total total = 0;
for (std::size_t i = 0; i < graph.arcs.size(); ++i) {
if (!(i & 1)) total += Total((Wide(graph.arcs[i].cap) - Wide(work.arcs[i].cap)) * Wide(costs[i]));
graph.arcs[i].cap = work.arcs[i].cap;
}
return {work.arcs.back().cap, total};
}
}
#endif
#line 1 "components/min_cost_circulation.hpp"
#line 1 "components/flow_graph.hpp"
#include <algorithm>
#include <cassert>
#include <cstddef>
#include <limits>
#include <vector>
namespace flow_detail {
template<class Cap> struct Graph {
struct Arc { int to; Cap cap; };
int n;
std::vector<Arc> arcs;
std::vector<int> start, order;
bool built = false;
explicit Graph(int n = 0) : n(n) { assert(n >= 0); }
void reserve(std::size_t m) { assert(m <= std::size_t(std::numeric_limits<int>::max() / 2)); arcs.reserve(2 * m); }
int add_edge(int from, int to, Cap cap) {
assert(0 <= from && from < n && 0 <= to && to < n && cap >= 0);
assert(arcs.size() <= std::size_t(std::numeric_limits<int>::max() - 2));
int id = int(arcs.size());
arcs.push_back({to, cap});
arcs.push_back({from, Cap(0)});
built = false;
return id / 2;
}
void build() {
if (built) return;
start.assign(std::size_t(n) + 1, 0);
for (std::size_t i = 0; i < arcs.size(); ++i) ++start[arcs[i ^ 1].to + 1];
for (int v = 0; v < n; ++v) start[v + 1] += start[v];
order.resize(arcs.size());
auto next = start;
for (int i = 0; i < int(arcs.size()); ++i) order[next[arcs[i ^ 1].to]++] = i;
built = true;
}
void reset_flow() {
for (std::size_t i = 0; i < arcs.size(); i += 2) arcs[i].cap += arcs[i + 1].cap, arcs[i + 1].cap = 0;
}
};
}
#line 1 "components/d_ary_heap.hpp"
#line 7 "components/d_ary_heap.hpp"
#include <functional>
#include <utility>
#line 10 "components/d_ary_heap.hpp"
template <class T, class Compare = std::less<T>, std::size_t D = 2>
class DaryHeap {
static_assert(D >= 2);
std::vector<T> data;
[[no_unique_address]] Compare compare{};
void up(std::size_t i, T value) {
while (i) {
std::size_t p = (i - 1) / D;
if (!compare(data[p], value)) break;
data[i] = std::move(data[p]);
i = p;
}
data[i] = std::move(value);
}
void down(std::size_t i, T value) {
std::size_t n = data.size();
while (i < n / D + (n % D > 1)) {
std::size_t best = i * D + 1, end = n;
if constexpr (D <= std::size_t(-1) / 2) end = std::min(best + D, n);
for (std::size_t j = best + 1; j < end; ++j)
if (compare(data[best], data[j])) best = j;
if (!compare(value, data[best])) break;
data[i] = std::move(data[best]);
i = best;
}
data[i] = std::move(value);
}
public:
DaryHeap() = default;
explicit DaryHeap(Compare compare) : compare(std::move(compare)) {}
explicit DaryHeap(std::vector<T> values, Compare compare = {}) : data(std::move(values)), compare(std::move(compare)) {
for (std::size_t i = data.size() / D + (data.size() % D > 1); i--;) down(i, std::move(data[i]));
}
bool empty() const { return data.empty(); }
std::size_t size() const { return data.size(); }
void reserve(std::size_t n) { data.reserve(n); }
void clear() { data.clear(); }
const std::vector<T> &values() const { return data; }
decltype(auto) top() const {
assert(!empty());
return data.front();
}
void push(T value) {
data.push_back(std::move(value));
up(data.size() - 1, std::move(data.back()));
}
T replace_top(T value) {
assert(!empty());
T result = std::move(data.front());
down(0, std::move(value));
return result;
}
T push_pop(T value) {
if (empty() || !compare(value, data.front())) return value;
return replace_top(std::move(value));
}
T pop_value() {
assert(!empty());
T result = std::move(data.front());
if (data.size() == 1) data.pop_back();
else {
T value = std::move(data.back());
data.pop_back();
down(0, std::move(value));
}
return result;
}
void pop() { pop_value(); }
void swap(DaryHeap &other) noexcept(noexcept(std::swap(compare, other.compare))) {
data.swap(other.data);
std::swap(compare, other.compare);
}
};
#line 6 "components/min_cost_circulation.hpp"
#include <bit>
#include <cstdint>
#line 9 "components/min_cost_circulation.hpp"
#include <type_traits>
#line 11 "components/min_cost_circulation.hpp"
namespace flow_detail {
template<class Cap, class Cost, class Total>
std::pair<Cap, Total> scaling_flow(Graph<Cap>& graph, const std::vector<Cost>& costs, int s, int t, Cap limit) {
using Wide = __int128_t;
using State = std::pair<Wide, int>;
using Unsigned = std::make_unsigned_t<Cap>;
Cap upper = 0;
for (int j = graph.start[s]; j < graph.start[s + 1]; ++j) upper += std::min<Cap>(limit - upper, graph.arcs[graph.order[j]].cap);
limit = upper;
if (!limit) return {Cap(0), Total(0)};
Graph<Cap> work = graph;
std::vector<Cap> capacity;
capacity.reserve(work.arcs.size() + 2);
Unsigned maximum = 0;
for (auto& e : work.arcs) capacity.push_back(e.cap), maximum = std::max(maximum, Unsigned(e.cap)), e.cap = 0;
std::vector<Wide> cost(costs.begin(), costs.end());
Wide largest = 0;
for (Cost c : costs) largest = std::max(largest, c < 0 ? -Wide(c) : Wide(c));
work.add_edge(t, s, 0);
capacity.push_back(limit), capacity.push_back(0);
cost.push_back(-Wide(graph.n) * largest - 1), cost.push_back(-cost.back());
maximum = std::max(maximum, Unsigned(limit));
work.build();
int n = graph.n;
std::vector<Wide> potential(n), distance(n);
std::vector<std::int64_t> excess(n);
std::vector<int> parent(n), settled, zero;
std::vector<unsigned char> visited(n);
DaryHeap<State, std::greater<State>, 4> heap;
heap.reserve(work.arcs.size());
settled.reserve(n), zero.reserve(n);
const Wide inf = std::numeric_limits<Wide>::max();
for (int bit = int(std::bit_width(maximum)); bit--;) {
for (std::size_t i = 0; i < capacity.size(); ++i) work.arcs[i].cap = work.arcs[i].cap * 2 + ((Unsigned(capacity[i]) >> bit) & 1);
std::fill(excess.begin(), excess.end(), 0);
for (int i = 0; i < int(work.arcs.size()); ++i) {
auto& e = work.arcs[i];
int from = work.arcs[i ^ 1].to;
if (e.cap && cost[i] + potential[from] - potential[e.to] < 0) {
auto amount = e.cap;
assert(amount == 1);
e.cap = 0, work.arcs[i ^ 1].cap += amount;
--excess[from], ++excess[e.to];
}
}
for (int source = 0; source < n; ++source) while (excess[source] > 0) {
std::fill(distance.begin(), distance.end(), inf);
std::fill(visited.begin(), visited.end(), 0);
heap.clear(), settled.clear(), zero.clear();
distance[source] = 0;
zero.push_back(source);
int target = -1;
while (!zero.empty() || !heap.empty()) {
int v;
if (!zero.empty()) v = zero.back(), zero.pop_back();
else {
auto [d, u] = heap.pop_value();
if (distance[u] != d) continue;
v = u;
}
if (visited[v]) continue;
visited[v] = 1, settled.push_back(v);
if (excess[v] < 0) { target = v; break; }
for (int j = work.start[v]; j < work.start[v + 1]; ++j) {
int i = work.order[j];
const auto& e = work.arcs[i];
if (!e.cap || visited[e.to]) continue;
Wide reduced = cost[i] + potential[v] - potential[e.to];
assert(reduced >= 0);
Wide next = distance[v] + reduced;
if (next >= distance[e.to]) continue;
distance[e.to] = next, parent[e.to] = i;
if (!reduced) zero.push_back(e.to);
else heap.push({next, e.to});
}
}
assert(target >= 0);
for (int v : settled) potential[v] -= distance[target] - distance[v];
std::int64_t amount = std::min(excess[source], -excess[target]);
for (int v = target; v != source; v = work.arcs[parent[v] ^ 1].to) {
auto cap = work.arcs[parent[v]].cap;
if (Unsigned(cap) < std::uint64_t(amount)) amount = std::int64_t(cap);
}
for (int v = target; v != source; v = work.arcs[parent[v] ^ 1].to) {
int i = parent[v];
work.arcs[i].cap -= Cap(amount), work.arcs[i ^ 1].cap += Cap(amount);
}
excess[source] -= amount, excess[target] += amount;
}
}
Total total = 0;
for (std::size_t i = 0; i < graph.arcs.size(); ++i) {
if (!(i & 1)) total += Total((Wide(graph.arcs[i].cap) - Wide(work.arcs[i].cap)) * Wide(costs[i]));
graph.arcs[i].cap = work.arcs[i].cap;
}
return {work.arcs.back().cap, total};
}
}