|
| 1 | +#pragma once |
| 2 | +#include <cassert> |
| 3 | +#include <vector> |
| 4 | + |
| 5 | +// Shortest path of Monge-weighted graph |
| 6 | +// Variant of LARSCH Algorithm: https://noshi91.hatenablog.com/entry/2023/02/18/005856 |
| 7 | +// Complexity: O(n log n) |
| 8 | +// |
| 9 | +// Given a directed graph with n vertices and weighted edges |
| 10 | +// (w(i, j) = cost_callback(i, j) (i < j)), |
| 11 | +// this class calculates the shortest path from vertex 0 to all other vertices. |
| 12 | +template <class Cost> struct monge_shortest_path { |
| 13 | + std::vector<Cost> dist; // dist[i] = shortest distance from 0 to i |
| 14 | + std::vector<int> amin; // amin[i] = previous vertex of i in the shortest path |
| 15 | + |
| 16 | + template <class F> void _check(int i, int k, F cost_callback) { |
| 17 | + if (i <= k) return; |
| 18 | + if (Cost c = dist[k] + cost_callback(k, i); c < dist[i]) dist[i] = c, amin[i] = k; |
| 19 | + } |
| 20 | + |
| 21 | + template <class F> void _rec_solve(int l, int r, F cost_callback) { |
| 22 | + if (r - l == 1) return; |
| 23 | + |
| 24 | + const int m = (l + r) / 2; |
| 25 | + for (int k = amin[l]; k <= amin[r]; ++k) _check(m, k, cost_callback); |
| 26 | + |
| 27 | + _rec_solve(l, m, cost_callback); |
| 28 | + for (int k = l + 1; k <= m; ++k) _check(r, k, cost_callback); |
| 29 | + _rec_solve(m, r, cost_callback); |
| 30 | + } |
| 31 | + |
| 32 | + template <class F> Cost solve(int n, F cost_callback) { |
| 33 | + assert(n > 0); |
| 34 | + dist.resize(n); |
| 35 | + amin.assign(n, 0); |
| 36 | + |
| 37 | + dist[0] = Cost(); |
| 38 | + for (int i = 1; i < n; ++i) dist[i] = cost_callback(0, i); |
| 39 | + |
| 40 | + _rec_solve(0, n - 1, cost_callback); |
| 41 | + |
| 42 | + return dist.back(); |
| 43 | + } |
| 44 | + |
| 45 | + template <class F> int num_edges() const { |
| 46 | + int ret = 0; |
| 47 | + for (int c = (int)amin.size() - 1; c >= 0; c = amin[c]) ++ret; |
| 48 | + return ret; |
| 49 | + } |
| 50 | +}; |
| 51 | + |
| 52 | +// Find shortest path length from 0 to n - 1 with k edges, min_edges <= k <= max_edges |
| 53 | +// https://noshi91.hatenablog.com/entry/2022/01/13/001217 |
| 54 | +template <class Cost, class F> |
| 55 | +Cost monge_shortest_path_with_specified_edges(int n, int min_edges, int max_edges, |
| 56 | + Cost max_abs_cost, F cost_callback) { |
| 57 | + |
| 58 | + assert(1 <= n); |
| 59 | + assert(0 <= min_edges); |
| 60 | + assert(min_edges <= max_edges); |
| 61 | + assert(max_edges <= n - 1); |
| 62 | + |
| 63 | + monge_shortest_path<Cost> msp; |
| 64 | + |
| 65 | + auto eval = [&](Cost p) -> Cost { |
| 66 | + msp.solve(n, [&](int i, int j) { return cost_callback(i, j) - p; }); |
| 67 | + return -msp.dist.back() - p * (p < 0 ? max_edges : min_edges); |
| 68 | + }; |
| 69 | + |
| 70 | + Cost lo = -max_abs_cost * 3, hi = max_abs_cost * 3; |
| 71 | + |
| 72 | + while (lo + 1 < hi) { |
| 73 | + Cost p = (lo + hi) / 2, f = eval(p), df = eval(p + 1) - f; |
| 74 | + if (df == Cost()) { |
| 75 | + return -f; |
| 76 | + } else { |
| 77 | + (df < Cost() ? lo : hi) = p; |
| 78 | + } |
| 79 | + } |
| 80 | + |
| 81 | + Cost flo = eval(lo), fhi = eval(hi); |
| 82 | + |
| 83 | + return flo < fhi ? -flo : -fhi; |
| 84 | +} |
0 commit comments