-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
334 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
--- | ||
data: | ||
_extendedDependsOn: [] | ||
_extendedRequiredBy: [] | ||
_extendedVerifiedWith: | ||
- icon: ':heavy_check_mark:' | ||
path: other_algorithms/test/monge_shortest_path.yuki705.test.cpp | ||
title: other_algorithms/test/monge_shortest_path.yuki705.test.cpp | ||
_isVerificationFailed: false | ||
_pathExtension: hpp | ||
_verificationStatusIcon: ':heavy_check_mark:' | ||
attributes: | ||
links: | ||
- https://noshi91.hatenablog.com/entry/2022/01/13/001217 | ||
- https://noshi91.hatenablog.com/entry/2023/02/18/005856 | ||
bundledCode: "#line 2 \"other_algorithms/monge_shortest_path.hpp\"\n#include <cassert>\n\ | ||
#include <vector>\n\n// Shortest path of Monge-weighted graph\n// Variant of LARSCH\ | ||
\ Algorithm: https://noshi91.hatenablog.com/entry/2023/02/18/005856\n// Complexity:\ | ||
\ O(n log n)\n//\n// Given a directed graph with n vertices and weighted edges\n\ | ||
// (w(i, j) = cost_callback(i, j) (i < j)),\n// this class calculates the shortest\ | ||
\ path from vertex 0 to all other vertices.\ntemplate <class Cost> struct monge_shortest_path\ | ||
\ {\n std::vector<Cost> dist; // dist[i] = shortest distance from 0 to i\n\ | ||
\ std::vector<int> amin; // amin[i] = previous vertex of i in the shortest\ | ||
\ path\n\n template <class F> void _check(int i, int k, F cost_callback) {\n\ | ||
\ if (i <= k) return;\n if (Cost c = dist[k] + cost_callback(k,\ | ||
\ i); c < dist[i]) dist[i] = c, amin[i] = k;\n }\n\n template <class F>\ | ||
\ void _rec_solve(int l, int r, F cost_callback) {\n if (r - l == 1) return;\n\ | ||
\n const int m = (l + r) / 2;\n for (int k = amin[l]; k <= amin[r];\ | ||
\ ++k) _check(m, k, cost_callback);\n\n _rec_solve(l, m, cost_callback);\n\ | ||
\ for (int k = l + 1; k <= m; ++k) _check(r, k, cost_callback);\n \ | ||
\ _rec_solve(m, r, cost_callback);\n }\n\n template <class F> Cost solve(int\ | ||
\ n, F cost_callback) {\n assert(n > 0);\n dist.resize(n);\n \ | ||
\ amin.assign(n, 0);\n\n dist[0] = Cost();\n for (int i = 1;\ | ||
\ i < n; ++i) dist[i] = cost_callback(0, i);\n\n _rec_solve(0, n - 1, cost_callback);\n\ | ||
\n return dist.back();\n }\n\n template <class F> int num_edges()\ | ||
\ const {\n int ret = 0;\n for (int c = (int)amin.size() - 1; c\ | ||
\ >= 0; c = amin[c]) ++ret;\n return ret;\n }\n};\n\n// Find shortest\ | ||
\ path length from 0 to n - 1 with k edges, min_edges <= k <= max_edges\n// https://noshi91.hatenablog.com/entry/2022/01/13/001217\n\ | ||
template <class Cost, class F>\nCost monge_shortest_path_with_specified_edges(int\ | ||
\ n, int min_edges, int max_edges,\n \ | ||
\ Cost max_abs_cost, F cost_callback) {\n\n assert(1 <= n);\n assert(0\ | ||
\ <= min_edges);\n assert(min_edges <= max_edges);\n assert(max_edges <=\ | ||
\ n - 1);\n\n monge_shortest_path<Cost> msp;\n\n auto eval = [&](Cost p)\ | ||
\ -> Cost {\n msp.solve(n, [&](int i, int j) { return cost_callback(i,\ | ||
\ j) - p; });\n return -msp.dist.back() - p * (p < 0 ? max_edges : min_edges);\n\ | ||
\ };\n\n Cost lo = -max_abs_cost * 3, hi = max_abs_cost * 3;\n\n while\ | ||
\ (lo + 1 < hi) {\n Cost p = (lo + hi) / 2, f = eval(p), df = eval(p +\ | ||
\ 1) - f;\n if (df == Cost()) {\n return -f;\n } else\ | ||
\ {\n (df < Cost() ? lo : hi) = p;\n }\n }\n\n Cost flo\ | ||
\ = eval(lo), fhi = eval(hi);\n\n return flo < fhi ? -flo : -fhi;\n}\n" | ||
code: "#pragma once\n#include <cassert>\n#include <vector>\n\n// Shortest path of\ | ||
\ Monge-weighted graph\n// Variant of LARSCH Algorithm: https://noshi91.hatenablog.com/entry/2023/02/18/005856\n\ | ||
// Complexity: O(n log n)\n//\n// Given a directed graph with n vertices and weighted\ | ||
\ edges\n// (w(i, j) = cost_callback(i, j) (i < j)),\n// this class calculates\ | ||
\ the shortest path from vertex 0 to all other vertices.\ntemplate <class Cost>\ | ||
\ struct monge_shortest_path {\n std::vector<Cost> dist; // dist[i] = shortest\ | ||
\ distance from 0 to i\n std::vector<int> amin; // amin[i] = previous vertex\ | ||
\ of i in the shortest path\n\n template <class F> void _check(int i, int k,\ | ||
\ F cost_callback) {\n if (i <= k) return;\n if (Cost c = dist[k]\ | ||
\ + cost_callback(k, i); c < dist[i]) dist[i] = c, amin[i] = k;\n }\n\n \ | ||
\ template <class F> void _rec_solve(int l, int r, F cost_callback) {\n \ | ||
\ if (r - l == 1) return;\n\n const int m = (l + r) / 2;\n for\ | ||
\ (int k = amin[l]; k <= amin[r]; ++k) _check(m, k, cost_callback);\n\n \ | ||
\ _rec_solve(l, m, cost_callback);\n for (int k = l + 1; k <= m; ++k)\ | ||
\ _check(r, k, cost_callback);\n _rec_solve(m, r, cost_callback);\n \ | ||
\ }\n\n template <class F> Cost solve(int n, F cost_callback) {\n assert(n\ | ||
\ > 0);\n dist.resize(n);\n amin.assign(n, 0);\n\n dist[0]\ | ||
\ = Cost();\n for (int i = 1; i < n; ++i) dist[i] = cost_callback(0, i);\n\ | ||
\n _rec_solve(0, n - 1, cost_callback);\n\n return dist.back();\n\ | ||
\ }\n\n template <class F> int num_edges() const {\n int ret = 0;\n\ | ||
\ for (int c = (int)amin.size() - 1; c >= 0; c = amin[c]) ++ret;\n \ | ||
\ return ret;\n }\n};\n\n// Find shortest path length from 0 to n - 1 with\ | ||
\ k edges, min_edges <= k <= max_edges\n// https://noshi91.hatenablog.com/entry/2022/01/13/001217\n\ | ||
template <class Cost, class F>\nCost monge_shortest_path_with_specified_edges(int\ | ||
\ n, int min_edges, int max_edges,\n \ | ||
\ Cost max_abs_cost, F cost_callback) {\n\n assert(1 <= n);\n assert(0\ | ||
\ <= min_edges);\n assert(min_edges <= max_edges);\n assert(max_edges <=\ | ||
\ n - 1);\n\n monge_shortest_path<Cost> msp;\n\n auto eval = [&](Cost p)\ | ||
\ -> Cost {\n msp.solve(n, [&](int i, int j) { return cost_callback(i,\ | ||
\ j) - p; });\n return -msp.dist.back() - p * (p < 0 ? max_edges : min_edges);\n\ | ||
\ };\n\n Cost lo = -max_abs_cost * 3, hi = max_abs_cost * 3;\n\n while\ | ||
\ (lo + 1 < hi) {\n Cost p = (lo + hi) / 2, f = eval(p), df = eval(p +\ | ||
\ 1) - f;\n if (df == Cost()) {\n return -f;\n } else\ | ||
\ {\n (df < Cost() ? lo : hi) = p;\n }\n }\n\n Cost flo\ | ||
\ = eval(lo), fhi = eval(hi);\n\n return flo < fhi ? -flo : -fhi;\n}\n" | ||
dependsOn: [] | ||
isVerificationFile: false | ||
path: other_algorithms/monge_shortest_path.hpp | ||
requiredBy: [] | ||
timestamp: '2024-10-06 15:03:01+09:00' | ||
verificationStatus: LIBRARY_ALL_AC | ||
verifiedWith: | ||
- other_algorithms/test/monge_shortest_path.yuki705.test.cpp | ||
documentation_of: other_algorithms/monge_shortest_path.hpp | ||
layout: document | ||
title: Shortest path of DAG with Monge weights | ||
--- | ||
|
||
$n$ 頂点の DAG で辺重みが Monge となるようなものに対して最短路長を高速に求める. [1] で紹介されている簡易版 LARSCH Algorithm が実装されていて,計算量は $O(n \log n)$ . | ||
|
||
また,辺数が `min_edges` 以上 `max_edges` 以下であるようなものに限った最短路長を高速に求めることも可能(計算量にさらに重み二分探索の $\log$ がつく). | ||
|
||
## 使用方法 | ||
|
||
### 最短路長の計算 | ||
|
||
```cpp | ||
auto f = [&](int s, int t) -> Cost { | ||
// | ||
}; | ||
|
||
monge_shortest_path<Cost> msp; | ||
Cost ret = msp.solve(n, f); | ||
``` | ||
|
||
### 辺の本数の下限・上限を指定した最短路長の計算 | ||
|
||
```cpp | ||
auto f = [&](int s, int t) -> Cost { | ||
// | ||
}; | ||
|
||
int n; // 頂点数 | ||
int l, r; // 辺の本数が [l, r] の範囲に収まる最短路を見つけたい | ||
Cost max_weight; // f() が返す値の絶対値の上界 | ||
|
||
Cost ret = monge_shortest_path_with_specified_edges(n, l, r, max_weight, f); | ||
``` | ||
|
||
## 問題例 | ||
|
||
- [No.705 ゴミ拾い Hard - yukicoder](https://yukicoder.me/problems/no/705) | ||
- [AtCoder Beginner Contest 218 H - Red and Blue Lamps](https://atcoder.jp/contests/abc218/tasks/abc218_h) | ||
- [東京海上日動プログラミングコンテスト2024(AtCoder Beginner Contest 355) G - Baseball](https://atcoder.jp/contests/abc355/tasks/abc355_g) | ||
- [東北大学プログラミングコンテスト 2022 K - Lebesgue Integral](https://atcoder.jp/contests/tupc2022/tasks/tupc2022_k) | ||
|
||
## Links | ||
|
||
- [1] [簡易版 LARSCH Algorithm - noshi91のメモ](https://noshi91.hatenablog.com/entry/2023/02/18/005856) | ||
- [2] [Aliens DP における二分探索の色々 - noshi91のメモ](https://noshi91.hatenablog.com/entry/2023/11/20/052227#fn-c9578a2a) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
89 changes: 89 additions & 0 deletions
89
other_algorithms/test/concave_max_plus_convolution.test.cpp.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
--- | ||
data: | ||
_extendedDependsOn: | ||
- icon: ':heavy_check_mark:' | ||
path: other_algorithms/smawk.hpp | ||
title: Totally Monotone Matrix Searching (SMAWK) | ||
_extendedRequiredBy: [] | ||
_extendedVerifiedWith: [] | ||
_isVerificationFailed: false | ||
_pathExtension: cpp | ||
_verificationStatusIcon: ':heavy_check_mark:' | ||
attributes: | ||
'*NOT_SPECIAL_COMMENTS*': '' | ||
PROBLEM: https://judge.yosupo.jp/problem/min_plus_convolution_convex_arbitrary | ||
links: | ||
- https://judge.yosupo.jp/problem/min_plus_convolution_convex_arbitrary | ||
bundledCode: "#line 1 \"other_algorithms/test/concave_max_plus_convolution.test.cpp\"\ | ||
\n#define PROBLEM \"https://judge.yosupo.jp/problem/min_plus_convolution_convex_arbitrary\"\ | ||
\n\n#line 2 \"other_algorithms/smawk.hpp\"\n#include <cassert>\n#include <functional>\n\ | ||
#include <numeric>\n#include <unordered_map>\n#include <utility>\n#include <vector>\n\ | ||
\n// SMAWK: finding minima of totally monotone function f(i, j) (0 <= i < N, 0\ | ||
\ <= j < M) for each i\n// Constraints: every submatrix of f(i, j) is monotone.\n\ | ||
// Complexity: O(N + M)\n// Option: `Memorization`: Memorize all query results\ | ||
\ using hashmaps, effective when each query\n// requires heavy complexity Rererence:\n\ | ||
// https://topcoder-g-hatena-ne-jp.jag-icpc.org/spaghetti_source/20120923/1348327542.html\n\ | ||
// http://web.cs.unlv.edu/larmore/Courses/CSC477/monge.pdf\n// Verify: https://codeforces.com/contest/1423/submission/98368491\n\ | ||
template <typename T, bool Memorization> struct SMAWK {\n std::vector<std::pair<int,\ | ||
\ T>> minima;\n std::function<T(int, int)> oracle;\n std::vector<std::unordered_map<int,\ | ||
\ T>> memo;\n T _query(int i, int j) {\n if (Memorization)\n \ | ||
\ return memo[i].count(j) ? memo[i][j] : (memo[i][j] = oracle(i, j));\n \ | ||
\ else\n return oracle(i, j);\n }\n\n void _smawk_rec(const\ | ||
\ std::vector<int> &js, int ib, int ie, int id) {\n if (ib > ie) return;\n\ | ||
\ std::vector<int> js2;\n int i = ib;\n for (auto j : js)\ | ||
\ {\n while (!js2.empty() and _query(i, js2.back()) >= _query(i, j))\ | ||
\ js2.pop_back(), i -= id;\n if (int(js2.size()) != (ie - ib) / id)\ | ||
\ js2.push_back(j), i += id;\n }\n _smawk_rec(js2, ib + id, ie,\ | ||
\ id * 2);\n\n for (int i = ib, q = 0; i <= ie; i += id * 2) {\n \ | ||
\ int jt = (i + id <= ie ? minima[i + id].first : js.back());\n \ | ||
\ T fm = 0;\n bool init = true;\n for (; q < int(js.size());\ | ||
\ ++q) {\n T fq = _query(i, js[q]);\n if (init or\ | ||
\ fm > fq) fm = fq, minima[i] = std::make_pair(js[q], fq);\n init\ | ||
\ = false;\n if (js[q] == jt) break;\n }\n }\n\ | ||
\ }\n SMAWK(int N, int M, std::function<T(int i, int j)> oracle_) : minima(N),\ | ||
\ oracle(oracle_) {\n if (Memorization) memo.resize(N);\n std::vector<int>\ | ||
\ js(M);\n std::iota(js.begin(), js.end(), 0);\n _smawk_rec(js,\ | ||
\ 0, N - 1, 1);\n }\n};\n\n// Concave max-plus convolution\n// b must be concave\n\ | ||
// Complexity: O(n + m)\n// Verify: https://www.codechef.com/problems/MAXPREFFLIP\n\ | ||
template <class S, S INF>\nstd::vector<S> concave_max_plus_convolution(const std::vector<S>\ | ||
\ &a, const std::vector<S> &b) {\n const int n = a.size(), m = b.size();\n\n\ | ||
\ auto is_concave = [&](const std::vector<S> &u) -> bool {\n for (int\ | ||
\ i = 1; i + 1 < int(u.size()); ++i) {\n if (u[i - 1] + u[i + 1] >\ | ||
\ u[i] + u[i]) return false;\n }\n return true;\n };\n\n bool\ | ||
\ a_concave = is_concave(a), b_concave = is_concave(b);\n assert(a_concave\ | ||
\ or b_concave);\n if (!b_concave) return concave_max_plus_convolution<S, INF>(b,\ | ||
\ a);\n\n auto select = [&](int i, int j) -> S {\n int aidx = j, bidx\ | ||
\ = i - j;\n if (bidx < 0 or bidx >= m or aidx >= n) return INF;\n \ | ||
\ return -(a[aidx] + b[bidx]);\n };\n SMAWK<S, false> sm(n + m - 1, n,\ | ||
\ select);\n std::vector<S> ret;\n for (auto x : sm.minima) ret.push_back(-x.second);\n\ | ||
\ return ret;\n}\n#line 4 \"other_algorithms/test/concave_max_plus_convolution.test.cpp\"\ | ||
\n\n#include <iostream>\n#line 7 \"other_algorithms/test/concave_max_plus_convolution.test.cpp\"\ | ||
\nusing namespace std;\n\nint main() {\n cin.tie(nullptr);\n ios::sync_with_stdio(false);\n\ | ||
\n int N, M;\n cin >> N >> M;\n vector<int> A(N), B(M);\n for (auto\ | ||
\ &a : A) cin >> a, a = -a;\n for (auto &b : B) cin >> b, b = -b;\n\n auto\ | ||
\ ret = concave_max_plus_convolution<int, ((1 << 30) - 1) * 2>(B, A);\n\n for\ | ||
\ (int i = 0; i < N + M - 1; ++i) cout << -ret[i] << \" \\n\"[i + 1 == N + M -\ | ||
\ 1];\n}\n" | ||
code: "#define PROBLEM \"https://judge.yosupo.jp/problem/min_plus_convolution_convex_arbitrary\"\ | ||
\n\n#include \"../smawk.hpp\"\n\n#include <iostream>\n#include <vector>\nusing\ | ||
\ namespace std;\n\nint main() {\n cin.tie(nullptr);\n ios::sync_with_stdio(false);\n\ | ||
\n int N, M;\n cin >> N >> M;\n vector<int> A(N), B(M);\n for (auto\ | ||
\ &a : A) cin >> a, a = -a;\n for (auto &b : B) cin >> b, b = -b;\n\n auto\ | ||
\ ret = concave_max_plus_convolution<int, ((1 << 30) - 1) * 2>(B, A);\n\n for\ | ||
\ (int i = 0; i < N + M - 1; ++i) cout << -ret[i] << \" \\n\"[i + 1 == N + M -\ | ||
\ 1];\n}\n" | ||
dependsOn: | ||
- other_algorithms/smawk.hpp | ||
isVerificationFile: true | ||
path: other_algorithms/test/concave_max_plus_convolution.test.cpp | ||
requiredBy: [] | ||
timestamp: '2024-10-06 15:03:32+09:00' | ||
verificationStatus: TEST_ACCEPTED | ||
verifiedWith: [] | ||
documentation_of: other_algorithms/test/concave_max_plus_convolution.test.cpp | ||
layout: document | ||
redirect_from: | ||
- /verify/other_algorithms/test/concave_max_plus_convolution.test.cpp | ||
- /verify/other_algorithms/test/concave_max_plus_convolution.test.cpp.html | ||
title: other_algorithms/test/concave_max_plus_convolution.test.cpp | ||
--- |
Oops, something went wrong.