#include "dp/monge-d-edge-shortest-path.hpp"
#pragma once #include "golden-section-search.hpp" #include "monge-shortest-path.hpp" // 辺コストが monge である DAG の D 辺 0-N 最短路 // f : from -> to のコスト (long long) // upper : max abs(辺数を 1 増減させたときのコストの変化) // (内部で int128 で計算しているので upper は 1e18 でも壊れない) long long monge_d_edge_shortest_path(int N, int D, long long upper, const function<long long(int, int)>& f) { using T = __int128_t; upper = abs(upper); auto dp = [&](long long x) -> T { auto g = [&](int from, int to) -> T { return f(from, to) + x; }; T cost = monge_shortest_path<T>(N, g)[N]; return cost - T{1} * D * x; }; auto [_, res] = golden_section_search<T, false>(dp, -upper, upper); return res; } /** * @brief monge グラフ上の d-辺最短路 */
#line 2 "dp/monge-d-edge-shortest-path.hpp" #line 2 "dp/golden-section-search.hpp" #include <cassert> #include <functional> #include <utility> using namespace std; // reference:https://twitter.com/noshi91/status/1399003086362865673 namespace golden_section_search_impl { using i64 = long long; // [min, max] は閉区間を入力する template <typename T, bool get_min = true> pair<i64, T> golden_section_search(const function<T(i64)>& f, i64 min, i64 max) { assert(min <= max); i64 a = min - 1, x, b; { i64 s = 1, t = 2; while (t < max - min + 2) swap(s += t, t); x = a + t - s, b = a + t; } T fx = f(x), fy; while (a + b != 2 * x) { i64 y = a + b - x; if (max < y || (fy = f(y), get_min ? fx < fy : fx > fy)) { b = a; a = y; } else { a = x; x = y; fx = fy; } } return {x, fx}; } } // namespace golden_section_search_impl using golden_section_search_impl::golden_section_search; /* @brief 黄金分割探索 */ #line 2 "dp/monge-shortest-path.hpp" #line 4 "dp/monge-shortest-path.hpp" #include <vector> using namespace std; // https://noshi91.hatenablog.com/entry/2023/02/18/005856 // 辺コストが monge である DAG の 0 - i 最短路 template <typename T> vector<T> monge_shortest_path(int N, const function<T(int, int)>& f) { T INF = (T{1} << (sizeof(T) * 8 - 2)) - 1; vector<T> dp(N + 1, INF); vector<int> x(N + 1, 0); auto check = [&](int from, int to) { if (from >= to) return; T cost = f(from, to); if (dp[from] + cost < dp[to]) dp[to] = dp[from] + cost, x[to] = from; }; auto dfs = [&](auto rc, int l, int r) -> void { if (l + 1 >= r) return; int m = (l + r) / 2; for (int i = x[l]; i <= x[r]; i++) check(i, m); rc(rc, l, m); for (int i = l + 1; i <= m; i++) check(i, r); rc(rc, m, r); }; dp[0] = 0, check(0, N), dfs(dfs, 0, N); return dp; } /** * @brief monge グラフ上の最短路 */ #line 5 "dp/monge-d-edge-shortest-path.hpp" // 辺コストが monge である DAG の D 辺 0-N 最短路 // f : from -> to のコスト (long long) // upper : max abs(辺数を 1 増減させたときのコストの変化) // (内部で int128 で計算しているので upper は 1e18 でも壊れない) long long monge_d_edge_shortest_path(int N, int D, long long upper, const function<long long(int, int)>& f) { using T = __int128_t; upper = abs(upper); auto dp = [&](long long x) -> T { auto g = [&](int from, int to) -> T { return f(from, to) + x; }; T cost = monge_shortest_path<T>(N, g)[N]; return cost - T{1} * D * x; }; auto [_, res] = golden_section_search<T, false>(dp, -upper, upper); return res; } /** * @brief monge グラフ上の d-辺最短路 */