diff --git a/DS/AssignZkwTree.h b/DS/AssignZkwTree.h index 597ff52..ba8a177 100644 --- a/DS/AssignZkwTree.h +++ b/DS/AssignZkwTree.h @@ -172,7 +172,9 @@ namespace OY { void resize(size_type length) { if (!(m_size = length)) return; m_dep = std::max(1, std::bit_width(m_size - 1)), m_cap = 1 << m_dep; - m_sub.assign(m_cap * 2, group::identity()); + node it{}; + it.m_val = group::identity(); + m_sub.assign(m_cap * 2, it); } template void resize(size_type length, InitMapping mapping) { diff --git a/DS/FastHeap.h b/DS/FastHeap.h index 06f62bc..5acc87d 100644 --- a/DS/FastHeap.h +++ b/DS/FastHeap.h @@ -81,38 +81,6 @@ namespace OY { bool empty() const { return !m_size; } size_type size() const { return m_size; } bool contains(size_type x) const { return ~m_pos[x]; } - void check() const { - if (empty()) return; - int L = m_size, R = m_size * 2 - 1, cnt = 0; - for (int i = 0; i < m_pos.size(); i++) - if (~m_pos[i]) { - if (!(L <= m_pos[i] and m_pos[i] <= R)) { - exit(1); - } - if (m_heap[m_pos[i]] != i) { - exit(2); - } - cnt++; - } - if (cnt != m_size) { - exit(3); - } - for (int i = m_size - 1; i; i--) { - if (m_heap[i] != m_heap[i * 2] and m_heap[i] != m_heap[i * 2 + 1]) { - exit(4); - } - if (m_heap[i] == m_heap[i * 2]) { - if (m_comp(m_map(m_heap[i * 2]), m_map(m_heap[i * 2 + 1]))) { - exit(5); - } - } - if (m_heap[i] == m_heap[i * 2 + 1]) { - if (m_comp(m_map(m_heap[i * 2 + 1]), m_map(m_heap[i * 2]))) { - exit(6); - } - } - } - } }; } template , typename HeapType = FHeap::Heap>, Compare>> diff --git a/DS/FastHeap.md b/DS/FastHeap.md index 997d976..6b90d74 100644 --- a/DS/FastHeap.md +++ b/DS/FastHeap.md @@ -2,6 +2,10 @@ ​ 数据结构:快速二叉堆。 +​ 练习题目: + +1. [find the safest road](https://acm.hdu.edu.cn/showproblem.php?pid=1596) + ### 二、模板功能 ​ 本堆核心为二叉堆,在此基础上做了一些优化。 diff --git a/DS/SiftHeap.md b/DS/SiftHeap.md index 482050e..cdbdcbf 100644 --- a/DS/SiftHeap.md +++ b/DS/SiftHeap.md @@ -2,6 +2,11 @@ ​ 数据结构:原地升降二叉堆。 +​ 练习题目: + +1. [find the safest road](https://acm.hdu.edu.cn/showproblem.php?pid=1596) + + ### 二、模板功能 ​ 本堆核心为二叉堆,在此基础上做了一些优化。 diff --git a/GRAPH/BellmanFord.h b/GRAPH/BellmanFord.h index b6d7e36..2d81068 100644 --- a/GRAPH/BellmanFord.h +++ b/GRAPH/BellmanFord.h @@ -61,7 +61,7 @@ namespace OY { using value_type = ValueType; using sum_type = ValueType; using compare_type = Compare; - static sum_type op(const sum_type &x, const sum_type &y) { return std::max(x, y); } + static sum_type op(const sum_type &x, const value_type &y) { return std::max(x, y); } static sum_type identity() { return {}; } static sum_type infinite() { return Inf; } }; diff --git a/GRAPH/BellmanFord.md b/GRAPH/BellmanFord.md index 03d5b28..bca06b6 100644 --- a/GRAPH/BellmanFord.md +++ b/GRAPH/BellmanFord.md @@ -4,10 +4,11 @@ ​ 练习题目: -1. [P1608 路径统计](https://www.luogu.com.cn/problem/P1608) -2. [P2047 [NOI2007] 社交网络](https://www.luogu.com.cn/problem/P2047) -3. [P3371 【模板】单源最短路径(弱化版)](https://www.luogu.com.cn/problem/P3371) -4. [P3385 【模板】负环](https://www.luogu.com.cn/problem/P3385) +1. [Deleting Edges](https://acm.hdu.edu.cn/showproblem.php?pid=6026) +2. [P1608 路径统计](https://www.luogu.com.cn/problem/P1608) +3. [P2047 [NOI2007] 社交网络](https://www.luogu.com.cn/problem/P2047) +4. [P3371 【模板】单源最短路径(弱化版)](https://www.luogu.com.cn/problem/P3371) +5. [P3385 【模板】负环](https://www.luogu.com.cn/problem/P3385) ### 二、模板功能 @@ -180,10 +181,6 @@ void test_distance_sum() { // 如果模板参数为 true,那么查询器还可以查询最短路的结点编号 using group = OY::BellmanFord::AddGroup; - // 第一个参数表示距离求和 - // 第二个参数表示不计数 - // 第三个参数表示求最小距离 - // 第四个参数表示要保存路径 auto table2 = G.calc(0).first; table2.trace( diff --git a/GRAPH/Dijkstra_heap.h b/GRAPH/Dijkstra.h similarity index 88% rename from GRAPH/Dijkstra_heap.h rename to GRAPH/Dijkstra.h index cbec6de..ea3f448 100644 --- a/GRAPH/Dijkstra_heap.h +++ b/GRAPH/Dijkstra.h @@ -6,8 +6,8 @@ gcc11.2,c++11 clang12.0,C++11 msvc14.2,C++14 */ -#ifndef __OY_DIJKSTRA_HEAP__ -#define __OY_DIJKSTRA_HEAP__ +#ifndef __OY_DIJKSTRA__ +#define __OY_DIJKSTRA__ #include #include @@ -15,7 +15,7 @@ msvc14.2,C++14 #include "../DS/FastHeap.h" namespace OY { - namespace DijkstraHeap { + namespace Dijkstra { using size_type = uint32_t; template struct DistanceNode { @@ -65,7 +65,7 @@ namespace OY { using value_type = ValueType; using sum_type = ValueType; using compare_type = Compare; - static sum_type op(const sum_type &x, const sum_type &y) { return std::max(x, y); } + static sum_type op(const sum_type &x, const value_type &y) { return std::max(x, y); } static sum_type identity() { return {}; } static sum_type infinite() { return Inf; } }; @@ -74,20 +74,21 @@ namespace OY { template bool operator()(const Tp1 &x, const Tp2 &y) const { return Compare()(y, x); } }; - template + template typename Heap = FastHeap> struct Solver { using group = Group; using value_type = typename group::value_type; using sum_type = typename group::sum_type; using compare_type = typename group::compare_type; + using heap_type = Heap, LessToGreater>; using node = DistanceNode; static constexpr bool has_count = !std::is_void::value; using count_type = typename std::conditional::type; size_type m_vertex_cnt; std::vector m_distance; - FastHeap, LessToGreater> m_heap; + heap_type m_heap; static sum_type infinite() { return group::infinite(); } - Solver(size_type vertex_cnt) : m_vertex_cnt(vertex_cnt), m_distance(vertex_cnt), m_heap(vertex_cnt, m_distance.data(), {}) { + Solver(size_type vertex_cnt) : m_vertex_cnt(vertex_cnt), m_distance(vertex_cnt), m_heap(vertex_cnt, m_distance.data()) { for (size_type i = 0; i != m_vertex_cnt; i++) { m_distance[i].m_val = infinite(); if constexpr (GetPath) m_distance[i].m_from = -1; @@ -98,13 +99,12 @@ namespace OY { if constexpr (has_count) m_distance[i].m_cnt = cnt; m_heap.push(i); } - template + template void run(size_type target, Traverser &&traverser) { while (!m_heap.empty()) { size_type from = m_heap.top(); m_heap.pop(); - if constexpr (Break) - if (from == target) break; + if (from == target) break; auto d = m_distance[from].m_val; if (!compare_type()(d, infinite())) break; traverser(from, [&](size_type to, const value_type &dis) { @@ -115,7 +115,7 @@ namespace OY { if constexpr (GetPath) m_distance[to].m_from = from; m_heap.push(to); } else if (!compare_type()(m_distance[to].m_val, to_dis)) - m_distance[to].m_cnt += m_distance[from].m_cnt, m_heap.push(to); + m_distance[to].m_cnt += m_distance[from].m_cnt; } else if (compare_type()(to_dis, m_distance[to].m_val)) { m_distance[to].m_val = to_dis; if constexpr (GetPath) m_distance[to].m_from = from; @@ -171,24 +171,21 @@ namespace OY { m_starts.assign(m_vertex_cnt + 1, {}); } void add_edge(size_type a, size_type b, Tp dis) { m_starts[a + 1]++, m_raw_edges.push_back({a, b, dis}); } - template , typename CountType = void, bool GetPath = false> - Solver calc(size_type source, size_type target = -1) const { + template , typename CountType = void, bool GetPath = false, template typename Heap = FastHeap> + Solver calc(size_type source, size_type target = -1) const { if (!m_prepared) _prepare(); - Solver sol(m_vertex_cnt); + Solver sol(m_vertex_cnt); sol.set_distance(source, Group::identity()); - if (~target) - sol.template run(target, *this); - else - sol.template run(-1, *this); + sol.run(target, *this); return sol; } - template > + template , template typename Heap = FastHeap> std::vector get_path(size_type source, size_type target) const { if (!m_prepared) _prepare(); std::vector res; - Solver sol(m_vertex_cnt); + Solver sol(m_vertex_cnt); sol.set_distance(source, Group::identity()); - sol.template run(target, *this); + sol.run(target, *this); res.push_back(source); sol.trace(target, [&](size_type from, size_type to) { res.push_back(to); }); return res; diff --git a/GRAPH/Dijkstra_heap.md b/GRAPH/Dijkstra.md similarity index 84% rename from GRAPH/Dijkstra_heap.md rename to GRAPH/Dijkstra.md index 4def267..eddeee8 100644 --- a/GRAPH/Dijkstra_heap.md +++ b/GRAPH/Dijkstra.md @@ -1,21 +1,23 @@ ### 一、模板类别 -​ 数据结构:`Dijkstra` 算法的堆实现。 +​ 数据结构:`Dijkstra` 算法。 ​ 练习题目: 1. [find the safest road](https://acm.hdu.edu.cn/showproblem.php?pid=1596) 2. [最短路径问题](https://acm.hdu.edu.cn/showproblem.php?pid=3790) -3. [1631. 最小体力消耗路径](https://leetcode.cn/problems/path-with-minimum-effort/) -4. [3123. 最短路径中的边](https://leetcode.cn/problems/find-edges-in-shortest-paths/) -5. [P1576 最小花费](https://www.luogu.com.cn/problem/P1576) -6. [P1608 路径统计](https://www.luogu.com.cn/problem/P1608) -7. [P2047 [NOI2007] 社交网络](https://www.luogu.com.cn/problem/P2047) -8. [P3371 【模板】单源最短路径(弱化版)](https://www.luogu.com.cn/problem/P3371) -9. [P4779 【模板】单源最短路径(标准版)](https://www.luogu.com.cn/problem/P4779) -10. [P6822 [PA2012] Tax](https://www.luogu.com.cn/problem/P6822) -11. [#622. 单源最短路径](https://uoj.ac/problem/622) -12. [Shortest Path](https://judge.yosupo.jp/problem/shortest_path)(https://github.com/yosupo06/library-checker-problems/issues/173) +3. [Deleting Edges](https://acm.hdu.edu.cn/showproblem.php?pid=6026) +4. [1631. 最小体力消耗路径](https://leetcode.cn/problems/path-with-minimum-effort/) +5. [3123. 最短路径中的边](https://leetcode.cn/problems/find-edges-in-shortest-paths/) +6. [P1491 集合位置](https://www.luogu.com.cn/problem/P1491) +7. [P1576 最小花费](https://www.luogu.com.cn/problem/P1576) +8. [P1608 路径统计](https://www.luogu.com.cn/problem/P1608) +9. [P2047 [NOI2007] 社交网络](https://www.luogu.com.cn/problem/P2047) +10. [P3371 【模板】单源最短路径(弱化版)](https://www.luogu.com.cn/problem/P3371) +11. [P4779 【模板】单源最短路径(标准版)](https://www.luogu.com.cn/problem/P4779) +12. [P6822 [PA2012] Tax](https://www.luogu.com.cn/problem/P6822) +13. [#622. 单源最短路径](https://uoj.ac/problem/622) +14. [Shortest Path](https://judge.yosupo.jp/problem/shortest_path)(https://github.com/yosupo06/library-checker-problems/issues/173) ### 二、模板功能 @@ -143,16 +145,16 @@ ### 三、模板示例 ```c++ -#include "GRAPH/Dijkstra_heap.h" +#include "GRAPH/Dijkstra.h" #include "IO/FastIO.h" #include "TEST/std_bit.h" void test_distance_sum() { - // 普通使用者只需要了解熟悉 OY::DijkstraHeap::Graph 的使用 + // 普通使用者只需要了解熟悉 OY::Dijkstra::Graph 的使用 cout << "test distance sum:\n"; // 建图 - OY::DijkstraHeap::Graph G(7, 9); + OY::Dijkstra::Graph G(7, 9); // 注意加的边都是有向边 G.add_edge(0, 1, 100); G.add_edge(0, 2, 200); @@ -171,11 +173,7 @@ void test_distance_sum() { cout << "min dis from 0 to 6:" << table.query(6) << endl; // 如果模板参数为 true,那么查询器还可以查询最短路的结点编号 - using group = OY::DijkstraHeap::AddGroup; - // 第一个参数表示距离求和 - // 第二个参数表示不计数 - // 第三个参数表示求最小距离 - // 第四个参数表示要保存路径 + using group = OY::Dijkstra::AddGroup; auto table2 = G.calc(0); table2.trace(6, [](int from, int to) { cout << "go from " << from << " -> " << to << endl; }); @@ -188,7 +186,7 @@ void test_distance_sum() { void test_distance_max() { cout << "test distance max:\n"; - OY::DijkstraHeap::Graph G(7, 9); + OY::Dijkstra::Graph G(7, 9); G.add_edge(0, 1, 100); G.add_edge(0, 2, 200); G.add_edge(3, 4, 100); @@ -201,7 +199,7 @@ void test_distance_max() { // 定义路径长度为路径中的边长的最大值 // 获取最短路查询器 - using group = OY::DijkstraHeap::MaxGroup; + using group = OY::Dijkstra::MaxGroup; auto table = G.calc(0); cout << "min dis from 0 to 0:" << table.query(0) << endl; cout << "min dis from 0 to 2:" << table.query(2) << endl; @@ -212,7 +210,7 @@ void test_distance_max() { void test_count() { cout << "test path count:\n"; - OY::DijkstraHeap::Graph G(4, 5); + OY::Dijkstra::Graph G(4, 5); G.add_edge(0, 1, 100); G.add_edge(1, 2, 200); G.add_edge(2, 3, 100); @@ -220,7 +218,7 @@ void test_count() { G.add_edge(1, 3, 300); // 获取最短路路径数查询器 - using monoid = OY::DijkstraHeap::AddGroup; + using monoid = OY::Dijkstra::AddGroup; auto table = G.calc(0); cout << "min dis from 0 to 3:" << table.query(3) << endl; cout << "path count:" << table.query_count(3) << endl; @@ -244,8 +242,8 @@ void test_solver() { adj[5].push_back({6, 200}); // 直接建一个可追溯最短路的解答器 - using group = OY::DijkstraHeap::AddGroup; - OY::DijkstraHeap::Solver sol(7); + using group = OY::Dijkstra::AddGroup; + OY::Dijkstra::Solver sol(7); sol.set_distance(0, 0); // 传递一个遍历边的泛型回调 sol.run(-1, [&](int from, auto call) { diff --git a/GRAPH/DijkstraKPath.h b/GRAPH/DijkstraKPath.h new file mode 100644 index 0000000..31c918c --- /dev/null +++ b/GRAPH/DijkstraKPath.h @@ -0,0 +1,209 @@ +/* +最后修改: +20241107 +测试环境: +gcc11.2,c++11 +clang12.0,C++11 +msvc14.2,C++14 +*/ +#ifndef __OY_DIJKSTRAKPATH__ +#define __OY_DIJKSTRAKPATH__ + +#include +#include +#include + +#include "../DS/FastHeap.h" + +namespace OY { + namespace DijkstraKPath { + using size_type = uint32_t; + template + struct DistanceNode { + Tp m_val; + CountType m_cnt; + size_type m_from; + }; + template + struct DistanceNode { + Tp m_val; + CountType m_cnt; + }; + template + struct DistanceNode { + Tp m_val; + size_type m_from; + }; + template + struct DistanceNode { + Tp m_val; + }; + template + struct Getter { + DistanceNode *m_sequence; + Getter(DistanceNode *sequence) : m_sequence(sequence) {} + const Tp &operator()(size_type index) const { return m_sequence[index].m_val; } + }; + template ::value || std::is_floating_point::value> + struct SafeInfinite { + static constexpr Tp max() { return std::numeric_limits::max() / 2; } + }; + template + struct SafeInfinite { + static constexpr Tp max() { return std::numeric_limits::max(); } + }; + template , SumType Inf = SafeInfinite::max()> + struct AddGroup { + using value_type = ValueType; + using sum_type = SumType; + using compare_type = Compare; + static sum_type op(const sum_type &x, const value_type &y) { return x + y; } + static sum_type identity() { return {}; } + static sum_type infinite() { return Inf; } + }; + template , ValueType Inf = SafeInfinite::max()> + struct MaxGroup { + using value_type = ValueType; + using sum_type = ValueType; + using compare_type = Compare; + static sum_type op(const sum_type &x, const sum_type &y) { return std::max(x, y); } + static sum_type identity() { return {}; } + static sum_type infinite() { return Inf; } + }; + template + struct LessToGreater { + template + bool operator()(const Tp1 &x, const Tp2 &y) const { return Compare()(y, x); } + }; + template + struct Solver { + using group = Group; + using value_type = typename group::value_type; + using sum_type = typename group::sum_type; + using compare_type = typename group::compare_type; + using node = DistanceNode; + static constexpr bool has_count = !std::is_void::value; + using count_type = typename std::conditional::type; + size_type m_vertex_cnt; + std::vector> m_distance; + FastHeap, LessToGreater> m_heap; + static sum_type infinite() { return group::infinite(); } + template + void _trace(const node *p, size_type x, Callback &&call) const { + size_type prev = p[x].m_from; + if (~prev) _trace(p, prev, call), call(prev / K, x / K); + } + Solver(size_type vertex_cnt) : m_vertex_cnt(vertex_cnt), m_distance(m_vertex_cnt), m_heap(m_vertex_cnt * K, m_distance.data()->data()) { + node inf{}; + inf.m_val = infinite(); + if constexpr (GetPath) inf.m_from = -1; + for (size_type i = 0; i != m_vertex_cnt; i++) m_distance[i].fill(inf); + } + void set_distance(size_type i, const sum_type &dis, count_type cnt = 1) { + m_distance[i][0].m_val = dis; + if constexpr (has_count) m_distance[i][0].m_cnt = cnt; + m_heap.push(i * K); + } + template + void run(size_type target, Traverser &&traverser) { + size_type last = (target + 1) * K - 1; + auto p = m_distance.data()->data(); + std::vector cursor(m_vertex_cnt); + while (!m_heap.empty()) { + size_type top = m_heap.top(); + m_heap.pop(); + if (top == last) break; + auto d = p[top].m_val; + if (!compare_type()(d, infinite())) break; + size_type from = top / K; + if (++cursor[from] != K && compare_type()(p[top + 1].m_val, infinite())) m_heap.push(top + 1); + traverser(from, [&](size_type to, const value_type &dis) { + sum_type to_dis = group::op(d, dis); + auto it = to * K + cursor[to], end = to * K + K; + while (it != end && compare_type()(p[it].m_val, to_dis)) it++; + if (it != end) + if constexpr (has_count) { + if (compare_type()(to_dis, p[it].m_val)) { + std::move_backward(p + it, p + end - 1, p + end); + p[it].m_val = to_dis, p[it].m_cnt = p[top].m_cnt; + if constexpr (GetPath) p[it].m_from = top; + if (it == to * K + cursor[to]) m_heap.push(it); + } else if (!compare_type()(p[it].m_val, to_dis)) + p[it].m_cnt += p[top].m_cnt; + } else if (compare_type()(to_dis, p[it].m_val)) { + std::move_backward(p + it, p + end - 1, p + end); + p[it].m_val = to_dis; + if constexpr (GetPath) p[it].m_from = top; + if (it == to * K + cursor[to]) m_heap.push(it); + } + }); + } + } + template + void trace(size_type target, size_type k, Callback &&call) const { _trace(m_distance.data()->data(), target * K + k, call); } + const sum_type &query(size_type target, size_type k) const { return m_distance[target][k].m_val; } + count_type query_count(size_type target, size_type k) const { + if constexpr (has_count) + return m_distance[target][k].m_cnt; + else + return compare_type()(m_distance[target][k].m_val, infinite()); + } + }; + template + struct Graph { + struct raw_edge { + size_type m_from, m_to; + Tp m_dis; + }; + struct edge { + size_type m_to; + Tp m_dis; + }; + size_type m_vertex_cnt; + mutable bool m_prepared; + mutable std::vector m_starts; + mutable std::vector m_edges; + std::vector m_raw_edges; + template + void operator()(size_type from, Callback &&call) const { + auto *first = m_edges.data() + m_starts[from], *last = m_edges.data() + m_starts[from + 1]; + for (auto it = first; it != last; ++it) call(it->m_to, it->m_dis); + } + void _prepare() const { + for (size_type i = 1; i != m_vertex_cnt + 1; i++) m_starts[i] += m_starts[i - 1]; + m_edges.resize(m_starts.back()); + auto cursor = m_starts; + for (auto &e : m_raw_edges) m_edges[cursor[e.m_from]++] = {e.m_to, e.m_dis}; + m_prepared = true; + } + Graph(size_type vertex_cnt = 0, size_type edge_cnt = 0) { resize(vertex_cnt, edge_cnt); } + void resize(size_type vertex_cnt, size_type edge_cnt) { + if (!(m_vertex_cnt = vertex_cnt)) return; + m_prepared = false, m_raw_edges.clear(), m_raw_edges.reserve(edge_cnt); + m_starts.assign(m_vertex_cnt + 1, {}); + } + void add_edge(size_type a, size_type b, Tp dis) { m_starts[a + 1]++, m_raw_edges.push_back({a, b, dis}); } + template , typename CountType = void, bool GetPath = false> + Solver calc(size_type source, size_type target = -1) const { + if (!m_prepared) _prepare(); + Solver sol(m_vertex_cnt); + sol.set_distance(source, Group::identity()); + sol.run(target, *this); + return sol; + } + template > + std::vector get_path(size_type source, size_type target, size_type k) const { + if (!m_prepared) _prepare(); + std::vector res; + Solver sol(m_vertex_cnt); + sol.set_distance(source, Group::identity()); + sol.run(target, *this); + res.push_back(source); + sol.trace(target, k, [&](size_type from, size_type to) { res.push_back(to); }); + return res; + } + }; + } +} + +#endif \ No newline at end of file diff --git a/GRAPH/DijkstraKPath.md b/GRAPH/DijkstraKPath.md new file mode 100644 index 0000000..4ddeaef --- /dev/null +++ b/GRAPH/DijkstraKPath.md @@ -0,0 +1,159 @@ +### 一、模板类别 + +​ 数据结构:`Dijkstra` 算法求严格 `K` 大路。 + +​ 练习题目: + +1. [How Many Paths Are There](https://acm.hdu.edu.cn/showproblem.php?pid=3191) +2. [Two Paths HDU](https://acm.hdu.edu.cn/showproblem.php?pid=6181) +3. [2045. 到达目的地的第二短时间](https://leetcode.cn/problems/second-minimum-time-to-reach-destination/) +4. [P2865 [USACO06NOV] Roadblocks G](https://www.luogu.com.cn/problem/P2865) + + + +### 二、模板功能 + +​ +​ 本模板与 `Dijkstra` 模板接口基本类似,区别在于本模板不仅仅求最短路,还可以求出次短路、第三短路等等。在求最短路时,传递模板参数 `K` 可以求出前 `K` 短路。 + +​ 本模板的次短路的意义为,允许重复走边、严格劣于最短路的路径长度。 + +​ 只有没有零环的情况下可以进行最短路计数;一般来说,仅在正权图、边权和路径长度情况下,才可以进行最短路计数。 + +### 三、模板示例 + +```c++ +#include "GRAPH/DijkstraKPath.h" +#include "IO/FastIO.h" +#include "TEST/std_bit.h" + +void test_distance_sum() { + // 普通使用者只需要了解熟悉 OY::Dijkstra::Graph 的使用 + cout << "test distance sum:\n"; + + // 建图 + OY::DijkstraKPath::Graph G(7, 10); + // 注意加的边都是有向边 + G.add_edge(0, 1, 100); + G.add_edge(0, 2, 200); + G.add_edge(2, 0, 1); + G.add_edge(3, 4, 100); + G.add_edge(3, 5, 100); + G.add_edge(0, 3, 95); + G.add_edge(6, 4, 100); + G.add_edge(4, 5, 190); + G.add_edge(5, 1, 100); + G.add_edge(5, 6, 200); + + // 获取最短路,次短路长度查询器 + auto table = G.calc<2>(0); + cout << "top2 dis from 0 to 0:" << table.query(0, 0) << ',' << table.query(0, 1) << endl; + cout << "top2 dis from 0 to 2:" << table.query(2, 0) << ',' << table.query(2, 1) << endl; + cout << "top2 dis from 0 to 6:" << table.query(6, 0) << ',' << table.query(6, 1) << endl; + + // 如果模板参数为 true,那么查询器还可以查询次短路的结点编号 + using group = OY::DijkstraKPath::AddGroup; + + auto table2 = G.calc<2, group, void, true>(0); + // 最短路路径 + cout << "No.1 path:\n"; + table2.trace(6, 0, [](int from, int to) { cout << "go from " << from << " -> " << to << endl; }); + cout << "No.2 path:\n"; + table2.trace(6, 1, [](int from, int to) { cout << "go from " << from << " -> " << to << endl; }); + + // G 本身有更方便的接口 + std::vector path1 = G.get_path(0, 6, 0); + for (int i = 0; i < path1.size(); i++) cout << path1[i] << (i + 1 == path1.size() ? "\n" : " -> "); + std::vector path2 = G.get_path(0, 6, 1); + for (int i = 0; i < path2.size(); i++) cout << path2[i] << (i + 1 == path2.size() ? "\n\n" : " -> "); +} + +void test_distance_max() { + cout << "test distance max:\n"; + + OY::DijkstraKPath::Graph G(7, 10); + G.add_edge(0, 1, 100); + G.add_edge(0, 2, 200); + G.add_edge(2, 0, 1); + G.add_edge(3, 4, 100); + G.add_edge(3, 5, 100); + G.add_edge(0, 3, 95); + G.add_edge(6, 4, 100); + G.add_edge(4, 5, 190); + G.add_edge(5, 1, 100); + G.add_edge(5, 6, 200); + + // 定义路径长度为路径中的边长的最大值 + // 获取最短路查询器 + using group = OY::DijkstraKPath::MaxGroup; + auto table = G.calc<2, group>(0); + cout << "top2 dis from 0 to 0:" << table.query(0, 0) << ',' << table.query(0, 1) << endl; + cout << "top2 dis from 0 to 2:" << table.query(2, 0) << ',' << table.query(2, 1) << endl; + cout << "top2 dis from 0 to 6:" << table.query(6, 0) << ',' << table.query(6, 1) << endl; + cout << endl; +} + +void test_count() { + cout << "test path count:\n"; + + OY::DijkstraKPath::Graph G(4, 7); + G.add_edge(0, 3, 100); + G.add_edge(0, 2, 200); + G.add_edge(0, 1, 120); + G.add_edge(1, 2, 80); + G.add_edge(2, 3, 300); + G.add_edge(2, 3, 300); + G.add_edge(2, 0, 1); + + // 获取最短路路径数查询器 + using monoid = OY::DijkstraKPath::AddGroup; + auto table = G.calc<3, monoid, int>(0); + cout << "No.1 dis from 0 to 3:" << table.query(3, 0) << endl; + cout << "path count:" << table.query_count(3, 0) << endl; + cout << "No.2 dis from 0 to 3:" << table.query(3, 1) << endl; + cout << "path count:" << table.query_count(3, 1) << endl; + cout << "No.3 dis from 0 to 3:" << table.query(3, 2) << endl; + cout << "path count:" << table.query_count(3, 2) << endl; + cout << endl; +} + +int main() { + test_distance_sum(); + test_distance_max(); + test_count(); +} +``` + +``` +#输出如下 +test distance sum: +top2 dis from 0 to 0:0,201 +top2 dis from 0 to 2:200,401 +top2 dis from 0 to 6:395,585 +No.1 path: +go from 0 -> 3 +go from 3 -> 5 +go from 5 -> 6 +No.2 path: +go from 0 -> 3 +go from 3 -> 4 +go from 4 -> 5 +go from 5 -> 6 +0 -> 3 -> 5 -> 6 +0 -> 3 -> 4 -> 5 -> 6 + +test distance max: +top2 dis from 0 to 0:0,200 +top2 dis from 0 to 2:200,200 +top2 dis from 0 to 6:200,1073741823 + +test path count: +No.1 dis from 0 to 3:100 +path count:1 +No.2 dis from 0 to 3:301 +path count:2 +No.3 dis from 0 to 3:500 +path count:4 + +``` + diff --git a/GRAPH/Dijkstra_naive.h b/GRAPH/Dijkstra_naive.h index 4885a9d..27f88fa 100644 --- a/GRAPH/Dijkstra_naive.h +++ b/GRAPH/Dijkstra_naive.h @@ -1,6 +1,6 @@ /* 最后修改: -20241028 +20241106 测试环境: gcc11.2,c++11 clang12.0,C++11 @@ -43,11 +43,14 @@ namespace OY { Tp m_val; bool m_visit; }; - template - struct Getter { - DistanceNode *m_sequence; - Getter(DistanceNode *sequence) : m_sequence(sequence) {} - const Tp &operator()(size_type index) const { return m_sequence[index].m_val; } + template + struct BaseLess { + bool operator()(const ValueType &x, const ValueType &y) const { return x < y; } + bool operator()(const SumType &x, const SumType &y) const { return x < y; } + }; + template + struct BaseLess { + bool operator()(const ValueType &x, const ValueType &y) const { return x < y; } }; template ::value || std::is_floating_point::value> struct SafeInfinite { @@ -57,143 +60,166 @@ namespace OY { struct SafeInfinite { static constexpr Tp max() { return std::numeric_limits::max(); } }; - template , SumType Inf = SafeInfinite::max()> + template , ValueType ValueInf = SafeInfinite::max(), SumType SumInf = SafeInfinite::max()> struct AddGroup { using value_type = ValueType; using sum_type = SumType; using compare_type = Compare; static sum_type op(const sum_type &x, const value_type &y) { return x + y; } - static sum_type identity() { return {}; } - static sum_type infinite() { return Inf; } + static value_type value_identity() { return {}; } + static sum_type sum_identity() { return {}; } + static value_type value_infinite() { return ValueInf; } + static sum_type sum_infinite() { return SumInf; } }; - template , ValueType Inf = SafeInfinite::max()> + template , ValueType Inf = SafeInfinite::max()> struct MaxGroup { using value_type = ValueType; using sum_type = ValueType; using compare_type = Compare; - static sum_type op(const sum_type &x, const sum_type &y) { return std::max(x, y); } - static sum_type identity() { return {}; } - static sum_type infinite() { return Inf; } + static sum_type op(const sum_type &x, const value_type &y) { return std::max(x, y); } + static value_type value_identity() { return {}; } + static sum_type sum_identity() { return {}; } + static value_type value_infinite() { return Inf; } + static sum_type sum_infinite() { return Inf; } }; - template - struct LessToGreater { - template - bool operator()(const Tp1 &x, const Tp2 &y) const { return Compare()(y, x); } + template + struct StaticContainerWrap { + template + struct type { + template + using result_type = Node[MAXN]; + static constexpr bool is_vector = false; + Tp m_data[MAXN][MAXN]; + size_type m_vertex_cnt; + void resize(size_type vertex_cnt, const Tp &infinite) { + m_vertex_cnt = vertex_cnt; + for (size_type i = 0; i != m_vertex_cnt; i++) std::fill_n(m_data[i], m_vertex_cnt, infinite); + } + Tp &get(size_type x, size_type y) { return m_data[x][y]; } + const Tp &get(size_type x, size_type y) const { return m_data[x][y]; } + template + void enumerate(size_type from, Callback &&call) const { + for (size_type to = 0; to != m_vertex_cnt; to++) call(to, m_data[from][to]); + } + }; }; - template - struct Solver { + template + struct VectorContainer { + template + using result_type = std::vector; + static constexpr bool is_vector = true; + std::vector m_data; + size_type m_vertex_cnt; + void resize(size_type vertex_cnt, const Tp &infinite) { + m_vertex_cnt = vertex_cnt; + m_data.assign(m_vertex_cnt * m_vertex_cnt, infinite); + } + Tp &get(size_type x, size_type y) { return m_data[m_vertex_cnt * x + y]; } + const Tp &get(size_type x, size_type y) const { return m_data[m_vertex_cnt * x + y]; } + template + void enumerate(size_type from, Callback &&call) const { + auto p = m_data.data() + m_vertex_cnt * from; + for (size_type to = 0; to != m_vertex_cnt; to++) call(to, p[to]); + } + }; + template typename Container = VectorContainer> + struct Graph : Container { using group = Group; using value_type = typename group::value_type; using sum_type = typename group::sum_type; using compare_type = typename group::compare_type; - using node = DistanceNode; - static constexpr bool has_count = !std::is_void::value; - using count_type = typename std::conditional::type; - size_type m_vertex_cnt; - std::vector m_distance; - static sum_type infinite() { return group::infinite(); } - Solver(size_type vertex_cnt) : m_vertex_cnt(vertex_cnt), m_distance(vertex_cnt) { - for (size_type i = 0; i != m_vertex_cnt; i++) { - m_distance[i].m_val = infinite(); - if constexpr (GetPath) m_distance[i].m_from = -1; - } - } - void set_distance(size_type i, const sum_type &dis, count_type cnt = 1) { - m_distance[i].m_val = dis; - if constexpr (has_count) m_distance[i].m_cnt = cnt; - } - template - void run(size_type target, Traverser &&traverser) { - while (true) { - size_type near = -1; - sum_type near_dis = infinite(); - for (size_type i = 0; i != m_vertex_cnt; i++) - if (!m_distance[i].m_visit && compare_type()(m_distance[i].m_val, near_dis)) near = i, near_dis = m_distance[i].m_val; - if (!~near || near == target) break; - m_distance[near].m_visit = true; - traverser(near, [&](size_type to, const value_type &dis) { + using base = Container; + template + struct solver { + static constexpr bool has_count = !std::is_void::value; + using count_type = typename std::conditional::type; + using node = DistanceNode; + typename base::result_type m_data; + static sum_type infinite() { return group::sum_infinite(); } + template + void _update(size_type near, const sum_type &near_dis, Traverser &&traverse) { + m_data[near].m_visit = true; + traverse.enumerate(near, [&](size_type to, const value_type &dis) { + if (to == near || !compare_type()(dis, group::value_infinite())) return; sum_type to_dis = group::op(near_dis, dis); if constexpr (has_count) { - if (compare_type()(to_dis, m_distance[to].m_val)) { - m_distance[to].m_val = to_dis, m_distance[to].m_cnt = m_distance[near].m_cnt; - if constexpr (GetPath) m_distance[to].m_from = near; - } else if (!compare_type()(m_distance[to].m_val, to_dis)) - m_distance[to].m_cnt += m_distance[near].m_cnt; - } else if (compare_type()(to_dis, m_distance[to].m_val)) { - m_distance[to].m_val = to_dis; - if constexpr (GetPath) m_distance[to].m_from = near; + if (compare_type()(to_dis, m_data[to].m_val)) { + m_data[to].m_val = to_dis, m_data[to].m_cnt = m_data[near].m_cnt; + if constexpr (GetPath) m_data[to].m_from = near; + } else if (!compare_type()(m_data[to].m_val, to_dis)) + m_data[to].m_cnt += m_data[near].m_cnt; + } else if (compare_type()(to_dis, m_data[to].m_val)) { + m_data[to].m_val = to_dis; + if constexpr (GetPath) m_data[to].m_from = near; } }); } - } - template - void trace(size_type target, Callback &&call) const { - size_type prev = m_distance[target].m_from; - if (~prev) trace(prev, call), call(prev, target); - } - const sum_type &query(size_type target) const { return m_distance[target].m_val; } - count_type query_count(size_type target) const { - if constexpr (has_count) - return m_distance[target].m_cnt; - else - return compare_type()(m_distance[target].m_val, infinite()); - } - }; - template - struct Graph { - struct raw_edge { - size_type m_from, m_to; - Tp m_dis; - }; - struct edge { - size_type m_to; - Tp m_dis; + solver(size_type n) { + node inf{}; + inf.m_val = infinite(), inf.m_visit = false; + if constexpr (GetPath) inf.m_from = -1; + if constexpr (base::is_vector) + m_data.assign(n, inf); + else + std::fill_n(m_data, n, inf); + } + void set_distance(size_type i, const sum_type &dis, count_type cnt = 1) { + m_data[i].m_val = dis; + if constexpr (has_count) m_data[i].m_cnt = cnt; + } + template + void trace(size_type target, Callback &&call) const { + size_type prev = m_data[target].m_from; + if (~prev) trace(prev, call), call(prev, target); + } + const sum_type &query(size_type target) const { return m_data[target].m_val; } + count_type query_count(size_type target) const { + if constexpr (has_count) + return m_data[target].m_cnt; + else + return compare_type()(m_data[target].m_val, infinite()); + } }; - size_type m_vertex_cnt; - mutable bool m_prepared; - mutable std::vector m_starts; - mutable std::vector m_edges; - std::vector m_raw_edges; - template - void operator()(size_type from, Callback &&call) const { - auto *first = m_edges.data() + m_starts[from], *last = m_edges.data() + m_starts[from + 1]; - for (auto it = first; it != last; ++it) call(it->m_to, it->m_dis); - } - void _prepare() const { - for (size_type i = 1; i != m_vertex_cnt + 1; i++) m_starts[i] += m_starts[i - 1]; - m_edges.resize(m_starts.back()); - auto cursor = m_starts; - for (auto &e : m_raw_edges) m_edges[cursor[e.m_from]++] = {e.m_to, e.m_dis}; - m_prepared = true; - } - Graph(size_type vertex_cnt = 0, size_type edge_cnt = 0) { resize(vertex_cnt, edge_cnt); } - void resize(size_type vertex_cnt, size_type edge_cnt) { - if (!(m_vertex_cnt = vertex_cnt)) return; - m_prepared = false, m_raw_edges.clear(), m_raw_edges.reserve(edge_cnt); - m_starts.assign(m_vertex_cnt + 1, {}); + static bool _update(value_type &val, const value_type &dis) { return compare_type()(dis, val) ? val = dis, true : false; } + Graph(size_type vertex_cnt = 0) { resize(vertex_cnt); } + void resize(size_type vertex_cnt) { + if (!vertex_cnt) return; + base::resize(vertex_cnt, group::value_infinite()); } - void add_edge(size_type a, size_type b, Tp dis) { m_starts[a + 1]++, m_raw_edges.push_back({a, b, dis}); } - template , typename CountType = void, bool GetPath = false> - Solver calc(size_type source, size_type target = -1) const { - if (!m_prepared) _prepare(); - Solver sol(m_vertex_cnt); - sol.set_distance(source, Group::identity()); - sol.run(target, *this); + void add_edge(size_type a, size_type b, value_type dis) { _update(base::get(a, b), dis); } + template + solver calc(size_type source, size_type target = -1) const { + size_type n = base::m_vertex_cnt; + solver sol(n); + sol.set_distance(source, group::sum_identity()); + sol._update(source, group::sum_identity(), *this); + while (true) { + size_type near = -1; + sum_type near_dis = group::sum_infinite(); + for (size_type i = 0; i != n; i++) + if (!sol.m_data[i].m_visit && compare_type()(sol.m_data[i].m_val, near_dis)) near = i, near_dis = sol.m_data[i].m_val; + if (!~near || near == target) break; + sol._update(near, near_dis, *this); + } return sol; } - template > std::vector get_path(size_type source, size_type target) const { - if (!m_prepared) _prepare(); + auto sol = calc(source, target); std::vector res; - Solver sol(m_vertex_cnt); - sol.set_distance(source, Group::identity()); - sol.run(target, *this); res.push_back(source); sol.trace(target, [&](size_type from, size_type to) { res.push_back(to); }); return res; } }; } + template , ValueType ValueInf = DijkstraNaive::SafeInfinite::max(), SumType SumInf = DijkstraNaive::SafeInfinite::max()> + using VectorAddDijkstraNaive = DijkstraNaive::Graph>; + template , ValueType ValueInf = DijkstraNaive::SafeInfinite::max(), SumType SumInf = DijkstraNaive::SafeInfinite::max(), DijkstraNaive::size_type MAXN = 1000> + using StaticAddDijkstraNaive = DijkstraNaive::Graph, DijkstraNaive::StaticContainerWrap::template type>; + template , ValueType Inf = DijkstraNaive::SafeInfinite::max()> + using VectorMaxDijkstraNaive = DijkstraNaive::Graph>; + template , ValueType Inf = DijkstraNaive::SafeInfinite::max(), DijkstraNaive::size_type MAXN = 1000> + using StaticMaxDijkstraNaive = DijkstraNaive::Graph, DijkstraNaive::StaticContainerWrap::template type>; } #endif \ No newline at end of file diff --git a/GRAPH/Dijkstra_naive.md b/GRAPH/Dijkstra_naive.md index 4909275..766fbed 100644 --- a/GRAPH/Dijkstra_naive.md +++ b/GRAPH/Dijkstra_naive.md @@ -4,131 +4,18 @@ ​ 练习题目: -1. [P1576 最小花费](https://www.luogu.com.cn/problem/P1576) -2. [P1608 路径统计](https://www.luogu.com.cn/problem/P1608) -3. [P2047 [NOI2007] 社交网络](https://www.luogu.com.cn/problem/P2047) -4. [P3371 【模板】单源最短路径(弱化版)](https://www.luogu.com.cn/problem/P3371) +1. [Deleting Edges](https://acm.hdu.edu.cn/showproblem.php?pid=6026) +2. [P1576 最小花费](https://www.luogu.com.cn/problem/P1576) +3. [P1608 路径统计](https://www.luogu.com.cn/problem/P1608) +4. [P2047 [NOI2007] 社交网络](https://www.luogu.com.cn/problem/P2047) +5. [P3371 【模板】单源最短路径(弱化版)](https://www.luogu.com.cn/problem/P3371) ### 二、模板功能 -​ 图论模板往往包含一个 `Solver` 和一个 `Graph` 。前者仅仅进行逻辑运算,而不包含图本身的数据;后者保存了图的点、边数据,并提供方便的接口。 +​ 本模板与 `Dijkstra` 模板功能基本类似,区别在于本模板不使用任何的堆或类堆数据结构,使用暴力方法找最小值;本模板的图存储方式也不是邻接表,而是邻接矩阵;本模板不会保存重边,如有重边,仅选择最优的一条边;本模板的 `Group` 参数不是在调用 `calc` 时指定,而要在创建图的时候就指定。 -​ 简单起见,使用者可以只使用 `Graph` 及其接口。 - -#### 1.构造图 - -1. 数据类型 - - 类型设定 `size_type = uint32_t` ,表示图中编号的类型。 - - 模板参数 `typename Tp` ,表示边权类型。 - - 构造参数 `size_type vertex_cnt` ,表示点数,默认为 `0` 。 - - 构造参数 `size_type edge_cnt` ,表示边数。若按有无向边,按两条边计。默认为 `0` 。 - -2. 时间复杂度 - - $O(n+m)$ 。 - -3. 备注 - - `Dijkstra` 算法处理的问题为有向图的最短路问题。 - - 如果图为无向图,需要将原图的边视为正反两个方向的有向边来构建。 - - 本数据结构不能处理带负权的边。 - - 本数据结构可以接受重边和自环。 - -#### 2.重置(resize) - -1. 数据类型 - - 输入参数 `size_type vertex_cnt` ,表示点数。 - - 输入参数 `size_type edge_cnt` ,表示边数。若按有无向边,按两条边计。 - -2. 时间复杂度 - - $O(n+m)$ 。 - -3. 备注 - - 本方法会强制清空之前的数据,并建立新图。 - -#### 3.加边(add_edge) - -1. 数据类型 - - 输入参数 `size_type a`​ ,表示边的起点编号。 - - 输入参数 `size_type b` ,表示边的终点编号。 - - 输入参数 `Tp dis` ,表示边权。 - -2. 时间复杂度 - - $O(1)$ 。 - -3. 备注 - - **注意:**在无向图中,需要按正反两个反向进行加边。 - -#### 4.获取最短路查询器(calc) - -1. 数据类型 - - 模板参数 `typename Group` ,表示描述路径类型的代数结构。 - - 模板参数 `typename CountType` ,表示最短路计数的类型。 - - 模板参数 `bool GetPath` ,表示在求最短路长度时,是否记录最短路路径。 - - 输入参数 `size_type source` ,表示起点编号。 - - 输入参数 `size_type target` ,表示终点编号。默认为 `-1` ,表示没有明确终点。 - - 返回类型 `Solver` ,表示用来计算和保存最短路的对象。 - -2. 时间复杂度 - - $O(n^2+m)$ 。 - -3. 备注 - - 可以通过返回的对象查询最短路长度,生成最短路路径。 - - 如果明确了终点,那么在获取到终点的最短路之后,会立即返回;也就是说比终点更远的点的最短路距离并不保证得到计算。 - - 模板参数 `Group` 规定了边权类型、路径长度类型、路径的默认长度、边权组合成路径长度的方式、路径长度的比较函数类型。若为 `AddGroup` 表示常规的边权和路径长度;若为 `MaxGroup` 表示以最大边权为路径长度。 - - 模板参数 `CountType` 规定了最短路的计数类型。由于最短路往往数量众多,往往传递自取模类型。若传递 `void` ,表示不进行计数。 - - 模板参数 `GetPath` 表示是否保存最短路路径。 - - **注意:**只有没有零环的情况下可以进行最短路计数;一般来说,仅在正权图、边权和路径长度情况下,才可以进行最短路计数。 - -#### 5.获取最短路(get_path) - -1. 数据类型 - - 模板参数 `typename Group` ,表示描述路径类型的半群。 - - 输入参数 `size_type source` ,表示起点编号。 - - 输入参数 `size_type target` ,表示终点编号。 - - 返回类型 `std::vector` ,表示获取到的最短路。 - -2. 时间复杂度 - - $O(n^2+m)$ 。 - -3. 备注 - - 本方法获取从给定起点出发到终点的边权和最小的路径。 +​ 在稠密图上,本模板优于 `Dijkstra` 的堆优化模板。 ### 三、模板示例 @@ -142,7 +29,7 @@ void test_distance_sum() { cout << "test distance sum:\n"; // 建图 - OY::DijkstraNaive::Graph G(7, 9); + OY::VectorAddDijkstraNaive G(7); // 注意加的边都是有向边 G.add_edge(0, 1, 100); G.add_edge(0, 2, 200); @@ -161,13 +48,8 @@ void test_distance_sum() { cout << "min dis from 0 to 6:" << table.query(6) << endl; // 如果模板参数为 true,那么查询器还可以查询最短路的结点编号 - using group = OY::DijkstraNaive::AddGroup; - // 第一个参数表示距离求和 - // 第二个参数表示不计数 - // 第三个参数表示求最小距离 - // 第四个参数表示要保存路径 - auto table2 = G.calc(0); + auto table2 = G.calc(0); table2.trace(6, [](int from, int to) { cout << "go from " << from << " -> " << to << endl; }); // G 本身有更方便的接口 @@ -178,7 +60,7 @@ void test_distance_sum() { void test_distance_max() { cout << "test distance max:\n"; - OY::DijkstraNaive::Graph G(7, 9); + OY::VectorMaxDijkstraNaive G(7); G.add_edge(0, 1, 100); G.add_edge(0, 2, 200); G.add_edge(3, 4, 100); @@ -191,8 +73,7 @@ void test_distance_max() { // 定义路径长度为路径中的边长的最大值 // 获取最短路查询器 - using group = OY::DijkstraNaive::MaxGroup; - auto table = G.calc(0); + auto table = G.calc(0); cout << "min dis from 0 to 0:" << table.query(0) << endl; cout << "min dis from 0 to 2:" << table.query(2) << endl; cout << "min dis from 0 to 6:" << table.query(6) << endl; @@ -202,7 +83,7 @@ void test_distance_max() { void test_count() { cout << "test path count:\n"; - OY::DijkstraNaive::Graph G(4, 5); + OY::VectorAddDijkstraNaive G(4); G.add_edge(0, 1, 100); G.add_edge(1, 2, 200); G.add_edge(2, 3, 100); @@ -210,54 +91,16 @@ void test_count() { G.add_edge(1, 3, 300); // 获取最短路路径数查询器 - using monoid = OY::DijkstraNaive::AddGroup; - auto table = G.calc(0); + auto table = G.calc(0); cout << "min dis from 0 to 3:" << table.query(3) << endl; cout << "path count:" << table.query_count(3) << endl; cout << endl; } -void test_solver() { -#if CPP_STANDARD >= 201402L - // 进阶使用者,可以把 Solver 用到自己的图里 - cout << "test solver:\n"; - // 这里以常见的二维 vector 存图举例 - std::vector>> adj(7); - adj[0].push_back({1, 100}); - adj[0].push_back({2, 200}); - adj[3].push_back({4, 100}); - adj[3].push_back({5, 100}); - adj[0].push_back({3, 95}); - adj[6].push_back({4, 100}); - adj[4].push_back({5, 190}); - adj[5].push_back({1, 100}); - adj[5].push_back({6, 200}); - - // 直接建一个可追溯最短路的解答器 - using group = OY::DijkstraNaive::AddGroup; - OY::DijkstraNaive::Solver sol(7); - sol.set_distance(0, 0); - // 传递一个遍历边的泛型回调 - sol.run(-1, [&](int from, auto call) { - for (auto to_and_dis : adj[from]) call(to_and_dis.first, to_and_dis.second); - }); - - // 查询最短路长度 - cout << "min dis from 0 to 0:" << sol.query(0) << endl; - cout << "min dis from 0 to 2:" << sol.query(2) << endl; - cout << "min dis from 0 to 6:" << sol.query(6) << endl; - - // 生成一个最短路径 - sol.trace(6, [](int from, int to) { cout << "from " << from << " to " << to << endl; }); - -#endif -} - int main() { test_distance_sum(); test_distance_max(); test_count(); - test_solver(); } ``` @@ -281,13 +124,5 @@ test path count: min dis from 0 to 3:400 path count:3 -test solver: -min dis from 0 to 0:0 -min dis from 0 to 2:200 -min dis from 0 to 6:395 -from 0 to 3 -from 3 to 5 -from 5 to 6 - ``` diff --git a/GRAPH/DynamicSPFA.h b/GRAPH/DynamicSPFA.h index 75bad09..21fd5bd 100644 --- a/GRAPH/DynamicSPFA.h +++ b/GRAPH/DynamicSPFA.h @@ -52,7 +52,7 @@ namespace OY { using value_type = ValueType; using sum_type = ValueType; using compare_type = Compare; - static sum_type op(const sum_type &x, const sum_type &y) { return std::max(x, y); } + static sum_type op(const sum_type &x, const value_type &y) { return std::max(x, y); } static sum_type identity() { return {}; } static sum_type infinite() { return Inf; } }; diff --git a/GRAPH/KthPath.md b/GRAPH/KthPath.md index ab0708f..3dd066d 100644 --- a/GRAPH/KthPath.md +++ b/GRAPH/KthPath.md @@ -4,8 +4,9 @@ ​ 练习题目: -1. [P2483 【模板】k 短路 / [SDOI2010] 魔法猪学院](https://www.luogu.com.cn/problem/P2483) -2. [K-Shortest Walk](https://judge.yosupo.jp/problem/k_shortest_walk)(https://github.com/yosupo06/library-checker-problems/issues/509) +1. [Two Paths HDU](https://acm.hdu.edu.cn/showproblem.php?pid=6181) +2. [P2483 【模板】k 短路 / [SDOI2010] 魔法猪学院](https://www.luogu.com.cn/problem/P2483) +3. [K-Shortest Walk](https://judge.yosupo.jp/problem/k_shortest_walk)(https://github.com/yosupo06/library-checker-problems/issues/509) ### 二、模板功能 diff --git a/GRAPH/SPFA.h b/GRAPH/SPFA.h index 8cac97a..18fe67a 100644 --- a/GRAPH/SPFA.h +++ b/GRAPH/SPFA.h @@ -65,7 +65,7 @@ namespace OY { using value_type = ValueType; using sum_type = ValueType; using compare_type = Compare; - static sum_type op(const sum_type &x, const sum_type &y) { return std::max(x, y); } + static sum_type op(const sum_type &x, const value_type &y) { return std::max(x, y); } static sum_type identity() { return {}; } static sum_type infinite() { return Inf; } }; diff --git a/GRAPH/SPFA.md b/GRAPH/SPFA.md index 279afd5..0d7aca8 100644 --- a/GRAPH/SPFA.md +++ b/GRAPH/SPFA.md @@ -4,13 +4,14 @@ ​ 练习题目: -1. [3123. 最短路径中的边](https://leetcode.cn/problems/find-edges-in-shortest-paths/) -2. [P1576 最小花费](https://www.luogu.com.cn/problem/P1576) -3. [P1608 路径统计](https://www.luogu.com.cn/problem/P1608) -4. [P2047 [NOI2007] 社交网络](https://www.luogu.com.cn/problem/P2047) -5. [P3371 【模板】单源最短路径(弱化版)](https://www.luogu.com.cn/problem/P3371) -6. [P3385 【模板】负环](https://www.luogu.com.cn/problem/P3385) -7. [P5960 【模板】差分约束](https://www.luogu.com.cn/problem/P5960) +1. [Deleting Edges](https://acm.hdu.edu.cn/showproblem.php?pid=6026) +2. [3123. 最短路径中的边](https://leetcode.cn/problems/find-edges-in-shortest-paths/) +3. [P1576 最小花费](https://www.luogu.com.cn/problem/P1576) +4. [P1608 路径统计](https://www.luogu.com.cn/problem/P1608) +5. [P2047 [NOI2007] 社交网络](https://www.luogu.com.cn/problem/P2047) +6. [P3371 【模板】单源最短路径(弱化版)](https://www.luogu.com.cn/problem/P3371) +7. [P3385 【模板】负环](https://www.luogu.com.cn/problem/P3385) +8. [P5960 【模板】差分约束](https://www.luogu.com.cn/problem/P5960) ### 二、模板功能 @@ -187,10 +188,6 @@ void test_distance_sum() { // 如果模板参数为 true,那么查询器还可以查询最短路的结点编号 using group = OY::SPFA::AddGroup; - // 第一个参数表示距离求和 - // 第二个参数表示不计数 - // 第三个参数表示求最小距离 - // 第四个参数表示要保存路径 auto table2 = G.calc(0).first; table2.trace(6, [](int from, int to) { cout << "go from " << from << " -> " << to << endl; }); diff --git a/GRAPH/SPFAKPath.h b/GRAPH/SPFAKPath.h new file mode 100644 index 0000000..ad4b387 --- /dev/null +++ b/GRAPH/SPFAKPath.h @@ -0,0 +1,230 @@ +/* +最后修改: +20241110 +测试环境: +gcc11.2,c++11 +clang12.0,C++11 +msvc14.2,C++14 +*/ +#ifndef __OY_SPFAKPATH__ +#define __OY_SPFAKPATH__ + +#include +#include +#include +#include +#include +#include +#include + +namespace OY { + namespace SPFAKPath { + using size_type = uint32_t; + template + struct DistanceNode { + Tp m_val; + CountType m_cnt, m_offer; + size_type m_from; + }; + template + struct DistanceNode { + Tp m_val; + CountType m_cnt, m_offer; + }; + template + struct DistanceNode { + Tp m_val; + size_type m_from; + }; + template + struct DistanceNode { + Tp m_val; + }; + template ::value || std::is_floating_point::value> + struct SafeInfinite { + static constexpr Tp max() { return std::numeric_limits::max() / 2; } + }; + template + struct SafeInfinite { + static constexpr Tp max() { return std::numeric_limits::max(); } + }; + template , SumType Inf = SafeInfinite::max()> + struct AddGroup { + using value_type = ValueType; + using sum_type = SumType; + using compare_type = Compare; + static sum_type op(const sum_type &x, const value_type &y) { return x + y; } + static sum_type identity() { return {}; } + static sum_type infinite() { return Inf; } + }; + template , ValueType Inf = SafeInfinite::max()> + struct MaxGroup { + using value_type = ValueType; + using sum_type = ValueType; + using compare_type = Compare; + static sum_type op(const sum_type &x, const value_type &y) { return std::max(x, y); } + static sum_type identity() { return {}; } + static sum_type infinite() { return Inf; } + }; + template + struct Solver { + using group = Group; + using value_type = typename group::value_type; + using sum_type = typename group::sum_type; + struct node : std::array, K> { + bool m_inside; + }; + using compare_type = typename group::compare_type; + static constexpr bool has_count = !std::is_void::value; + using count_type = typename std::conditional::type; + size_type m_vertex_cnt; + std::vector m_distance; + std::vector m_queue; + static sum_type infinite() { return group::infinite(); } + template + void _trace(size_type x, size_type k, Callback &&call) const { + size_type prev = m_distance[x][k].m_from; + if (~prev) _trace(prev / K, prev % K, call), call(prev / K, x); + } + Solver(size_type vertex_cnt) : m_vertex_cnt(vertex_cnt), m_distance(vertex_cnt), m_queue(vertex_cnt) { + node inf{}; + for (size_type k = 0; k != K; k++) { + inf[k].m_val = infinite(); + if constexpr (GetPath) inf[k].m_from = -1; + } + m_distance.assign(m_vertex_cnt, inf); + } + void set_distance(size_type i, const sum_type &dis, count_type cnt = 1) { + m_distance[i][0].m_val = dis; + if constexpr (has_count) m_distance[i][0].m_cnt = m_distance[i][0].m_offer = cnt; + } + template + bool run(Traverser &&traverser) { + for (size_type k = 0; k != K; k++) { + size_type head = 0, tail = 0; + for (size_type i = 0; i != m_vertex_cnt; i++) + if (compare_type()(m_distance[i][k].m_val, infinite())) { + m_queue[tail++] = i; + if (tail == m_vertex_cnt) tail = 0; + m_distance[i].m_inside = true; + } + for (size_type i = 0; i != m_vertex_cnt && m_distance[m_queue[head]].m_inside; i++) { + size_type len = tail <= head ? tail + m_vertex_cnt - head : tail - head; + while (len--) { + size_type from = m_queue[head++]; + if (head == m_vertex_cnt) head = 0; + m_distance[from].m_inside = false; + traverser(from, [&](size_type to, const value_type &dis) { + sum_type to_dis = group::op(m_distance[from][k].m_val, dis); + auto &x = m_distance[to]; + auto it = k; + while (it != K && compare_type()(x[it].m_val, to_dis)) it++; + if (it != K) + if constexpr (has_count) { + if (compare_type()(to_dis, x[it].m_val)) { + std::move_backward(x.data() + it, x.data() + K - 1, x.data() + K); + x[it].m_val = to_dis, x[it].m_cnt = x[it].m_offer = m_distance[from][k].m_offer; + if constexpr (GetPath) x[it].m_from = from * K + k; + if (it == k && !x.m_inside) { + x.m_inside = true, m_queue[tail++] = to; + if (tail == m_vertex_cnt) tail = 0; + } + } else if (!compare_type()(x[it].m_val, to_dis)) { + x[it].m_cnt += m_distance[from][k].m_offer, x[it].m_offer += m_distance[from][k].m_offer; + if (it == k && !x.m_inside) { + x.m_inside = true, m_queue[tail++] = to; + if (tail == m_vertex_cnt) tail = 0; + } + } + } else if (compare_type()(to_dis, x[it].m_val)) { + std::move_backward(x.data() + it, x.data() + K - 1, x.data() + K); + x[it].m_val = to_dis; + if constexpr (GetPath) x[it].m_from = from * K + k; + if (it == k && !x.m_inside) { + x.m_inside = true, m_queue[tail++] = to; + if (tail == m_vertex_cnt) tail = 0; + } + } + }); + if constexpr (has_count) m_distance[from][k].m_offer = 0; + } + } + if (m_distance[m_queue[head]].m_inside) return false; + } + return true; + } + template + void trace(size_type target, size_type k, Callback &&call) const { _trace(target, k, call); } + const sum_type &query(size_type target, size_type k) const { return m_distance[target][k].m_val; } + count_type query_count(size_type target, size_type k) const { + if constexpr (has_count) + return m_distance[target][k].m_cnt; + else + return compare_type()(m_distance[target][k].m_val, infinite()); + } + }; + template + struct Graph { + struct raw_edge { + size_type m_from, m_to; + Tp m_dis; + }; + struct edge { + size_type m_to; + Tp m_dis; + }; + size_type m_vertex_cnt; + mutable bool m_prepared; + mutable std::vector m_starts; + mutable std::vector m_edges; + std::vector m_raw_edges; + template + void operator()(size_type from, Callback &&call) const { + auto *first = m_edges.data() + m_starts[from], *last = m_edges.data() + m_starts[from + 1]; + for (auto it = first; it != last; ++it) call(it->m_to, it->m_dis); + } + void _prepare() const { + for (size_type i = 1; i != m_vertex_cnt + 1; i++) m_starts[i] += m_starts[i - 1]; + m_edges.resize(m_starts.back()); + auto cursor = m_starts; + for (auto &e : m_raw_edges) m_edges[cursor[e.m_from]++] = {e.m_to, e.m_dis}; + m_prepared = true; + } + Graph(size_type vertex_cnt = 0, size_type edge_cnt = 0) { resize(vertex_cnt, edge_cnt); } + void resize(size_type vertex_cnt, size_type edge_cnt) { + if (!(m_vertex_cnt = vertex_cnt)) return; + m_prepared = false, m_raw_edges.clear(), m_raw_edges.reserve(edge_cnt); + m_starts.assign(m_vertex_cnt + 1, {}); + } + void add_edge(size_type a, size_type b, Tp dis) { m_starts[a + 1]++, m_raw_edges.push_back({a, b, dis}); } + template , typename CountType = void, bool GetPath = false> + std::pair, bool> calc(size_type source) const { + if (!m_prepared) _prepare(); + auto res = std::make_pair(Solver(m_vertex_cnt), false); + res.first.set_distance(source, Group::identity()); + res.second = res.first.run(*this); + return res; + } + template > + bool has_negative_cycle(size_type source) const { + if (!m_prepared) _prepare(); + Solver<1, Group> sol(m_vertex_cnt); + sol.set_distance(source, Group::identity()); + return !sol.run(*this); + } + template > + std::vector get_path(size_type source, size_type target, size_type k) const { + if (!m_prepared) _prepare(); + std::vector res; + Solver sol(m_vertex_cnt); + sol.set_distance(source, Group::identity()); + if (!sol.run(*this)) return res; + res.push_back(source); + sol.trace(target, k, [&](size_type from, size_type to) { res.push_back(to); }); + return res; + } + }; + } +} + +#endif \ No newline at end of file diff --git a/GRAPH/SPFAKPath.md b/GRAPH/SPFAKPath.md new file mode 100644 index 0000000..f2d4274 --- /dev/null +++ b/GRAPH/SPFAKPath.md @@ -0,0 +1,159 @@ +### 一、模板类别 + +​ 数据结构:`SPFA` 算法求严格 `K` 大路。 + +​ 练习题目: + +1. [How Many Paths Are There](https://acm.hdu.edu.cn/showproblem.php?pid=3191) +2. [Two Paths HDU](https://acm.hdu.edu.cn/showproblem.php?pid=6181) +3. [2045. 到达目的地的第二短时间](https://leetcode.cn/problems/second-minimum-time-to-reach-destination/) +4. [P2865 [USACO06NOV] Roadblocks G](https://www.luogu.com.cn/problem/P2865) + + +### 二、模板功能 + +​ +​ 本模板与 `SPFA` 模板接口基本类似,区别在于本模板不仅仅求最短路,还可以求出次短路、第三短路等等。在求最短路时,传递模板参数 `K` 可以求出前 `K` 短路。 + +​ 本模板的次短路的意义为,允许重复走边、严格劣于最短路的路径长度。 + +​ 只有没有零环的情况下可以进行最短路计数;一般来说,仅在正权图、边权和路径长度情况下,才可以进行最短路计数。 + +### 三、模板示例 + +```c++ +#include "GRAPH/DijkstraKPath.h" +#include "GRAPH/SPFAKPath.h" +#include "IO/FastIO.h" +#include "TEST/std_bit.h" + +void test_distance_sum() { + // 普通使用者只需要了解熟悉 OY::SPFAKPath::Graph 的使用 + cout << "test distance sum:\n"; + + // 建图 + OY::SPFAKPath::Graph G(7, 10); + // 注意加的边都是有向边 + G.add_edge(0, 1, 100); + G.add_edge(0, 2, 200); + G.add_edge(2, 0, 1); + G.add_edge(3, 4, 100); + G.add_edge(3, 5, 100); + G.add_edge(0, 3, 95); + G.add_edge(6, 4, 100); + G.add_edge(4, 5, 190); + G.add_edge(5, 1, 100); + G.add_edge(5, 6, 200); + + // 获取最短路,次短路长度查询器 + auto table = G.calc<2>(0).first; + cout << "top2 dis from 0 to 0:" << table.query(0, 0) << ',' << table.query(0, 1) << endl; + cout << "top2 dis from 0 to 2:" << table.query(2, 0) << ',' << table.query(2, 1) << endl; + cout << "top2 dis from 0 to 6:" << table.query(6, 0) << ',' << table.query(6, 1) << endl; + + // 如果模板参数为 true,那么查询器还可以查询次短路的结点编号 + using group = OY::SPFAKPath::AddGroup; + + auto table2 = G.calc<2, group, void, true>(0).first; + // 最短路路径 + cout << "No.1 path:\n"; + table2.trace(6, 0, [](int from, int to) { cout << "go from " << from << " -> " << to << endl; }); + cout << "No.2 path:\n"; + table2.trace(6, 1, [](int from, int to) { cout << "go from " << from << " -> " << to << endl; }); + + // G 本身有更方便的接口 + std::vector path1 = G.get_path(0, 6, 0); + for (int i = 0; i < path1.size(); i++) cout << path1[i] << (i + 1 == path1.size() ? "\n" : " -> "); + std::vector path2 = G.get_path(0, 6, 1); + for (int i = 0; i < path2.size(); i++) cout << path2[i] << (i + 1 == path2.size() ? "\n\n" : " -> "); +} + +void test_distance_max() { + cout << "test distance max:\n"; + + OY::SPFAKPath::Graph G(7, 10); + G.add_edge(0, 1, 100); + G.add_edge(0, 2, 200); + G.add_edge(2, 0, 1); + G.add_edge(3, 4, 100); + G.add_edge(3, 5, 100); + G.add_edge(0, 3, 95); + G.add_edge(6, 4, 100); + G.add_edge(4, 5, 190); + G.add_edge(5, 1, 100); + G.add_edge(5, 6, 200); + + // 定义路径长度为路径中的边长的最大值 + // 获取最短路查询器 + using group = OY::SPFAKPath::MaxGroup; + auto table = G.calc<2, group>(0).first; + cout << "top2 dis from 0 to 0:" << table.query(0, 0) << ',' << table.query(0, 1) << endl; + cout << "top2 dis from 0 to 2:" << table.query(2, 0) << ',' << table.query(2, 1) << endl; + cout << "top2 dis from 0 to 6:" << table.query(6, 0) << ',' << table.query(6, 1) << endl; + cout << endl; +} + +void test_count() { + cout << "test path count:\n"; + + OY::SPFAKPath::Graph G(4, 7); + G.add_edge(0, 3, 100); + G.add_edge(0, 2, 200); + G.add_edge(0, 1, 120); + G.add_edge(1, 2, 80); + G.add_edge(2, 3, 300); + G.add_edge(2, 3, 300); + G.add_edge(2, 0, 1); + + // 获取最短路路径数查询器 + using monoid = OY::SPFAKPath::AddGroup; + auto table = G.calc<3, monoid, int>(0).first; + cout << "No.1 dis from 0 to 3:" << table.query(3, 0) << endl; + cout << "path count:" << table.query_count(3, 0) << endl; + cout << "No.2 dis from 0 to 3:" << table.query(3, 1) << endl; + cout << "path count:" << table.query_count(3, 1) << endl; + cout << "No.3 dis from 0 to 3:" << table.query(3, 2) << endl; + cout << "path count:" << table.query_count(3, 2) << endl; + cout << endl; +} + +int main() { + test_distance_sum(); + test_distance_max(); + test_count(); +} +``` + +``` +#输出如下 +test distance sum: +top2 dis from 0 to 0:0,201 +top2 dis from 0 to 2:200,401 +top2 dis from 0 to 6:395,585 +No.1 path: +go from 0 -> 3 +go from 3 -> 5 +go from 5 -> 6 +No.2 path: +go from 0 -> 3 +go from 3 -> 4 +go from 4 -> 5 +go from 5 -> 6 +0 -> 3 -> 5 -> 6 +0 -> 3 -> 4 -> 5 -> 6 + +test distance max: +top2 dis from 0 to 0:0,200 +top2 dis from 0 to 2:200,200 +top2 dis from 0 to 6:200,200 + +test path count: +No.1 dis from 0 to 3:100 +path count:1 +No.2 dis from 0 to 3:301 +path count:2 +No.3 dis from 0 to 3:500 +path count:4 + +``` + diff --git a/GRAPH/TopologicalSort.md b/GRAPH/TopologicalSort.md index a6990eb..8e423ea 100644 --- a/GRAPH/TopologicalSort.md +++ b/GRAPH/TopologicalSort.md @@ -4,7 +4,8 @@ ​ 练习题目: -1. [B3644 【模板】拓扑排序 / 家谱树](https://www.luogu.com.cn/problem/B3644) +1. [How Many Paths Are There](https://acm.hdu.edu.cn/showproblem.php?pid=3191) +2. [B3644 【模板】拓扑排序 / 家谱树](https://www.luogu.com.cn/problem/B3644) ### 二、模板功能 diff --git a/MATH/KthRoot.h b/MATH/KthRoot.h index 69c373a..1f6979e 100644 --- a/MATH/KthRoot.h +++ b/MATH/KthRoot.h @@ -55,7 +55,14 @@ namespace OY { if (k > 40) return 2; if (k >= 32) return 2 + (x >= pow3[k - 32]); if (k == 1) return x; - if (k == 2) return std::sqrt((long double)x); + if (k == 2) { +#ifdef _MSC_VER + uint64_t res = std::sqrt(x); + return res - ((res >> 32) || res * res > x); +#else + return std::sqrt((long double)x); +#endif + } double res = std::pow(x, floor_einv[k - 3]); if (fast_pow(res + 0.05, k) - 1 < x) res += 0.05; return res; diff --git a/MATH/SqrtDecomposition.h b/MATH/SqrtDecomposition.h index c227eae..797a946 100644 --- a/MATH/SqrtDecomposition.h +++ b/MATH/SqrtDecomposition.h @@ -75,9 +75,14 @@ namespace OY { SqrtDecomposition(Tp val) : m_val(val) {} Tp size() const { Tp r; - if constexpr (sizeof(Tp) == 8) + if constexpr (sizeof(Tp) == 8) { +#ifdef _MSC_VER + r = std::sqrt(val); + r -= r * r > val; +#else r = std::sqrt((long double)m_val); - else +#endif + } else r = std::sqrt((double)m_val); return r * 2 - (m_val * 2 + 1 < (r + 1) * r * 2); } diff --git a/MISC/DigitDP.h b/MISC/DigitDP.h index f40058e..adbe833 100644 --- a/MISC/DigitDP.h +++ b/MISC/DigitDP.h @@ -42,6 +42,54 @@ namespace OY { } }; template <> + struct StaticIntegerString<2> { + std::vector m_str; + size_type m_size; + static constexpr uint32_t base() { return 2; } + StaticIntegerString() = default; + StaticIntegerString(uint64_t val) { + m_str.assign(1, val); + m_size = val ? std::bit_width(val) : 1; + } + StaticIntegerString(std::string str) { + size_type i = str.find('1'); + if (i == size_type(std::string::npos)) + assign(1); + else { + assign(str.size() - i); + for (size_type j = str.size() - 1; j != i - 1; j--) set(str.size() - 1 - j, str[j] - '0'); + } + } + explicit StaticIntegerString(const char *str) : StaticIntegerString(std::string(str)) {} + size_type size() const { return m_size; } + void assign(size_type n) { m_str.assign((n + 63) >> 6, 0), m_size = n; } + uint32_t get(size_type i) const { return m_str[i >> 6] >> (i & 63) & 1; } + void set(size_type i, uint32_t val) { + if (val) + m_str[i >> 6] |= uint64_t(1) << (i & 63); + else + m_str[i >> 6] &= ~(uint64_t(1) << (i & 63)); + } + void push_high(uint32_t val) { + if (!(m_size++ & 63)) + m_str.push_back(val); + else + set(m_size - 1, val); + } + void pop_high() { + if (!(--m_size & 63)) + m_str.pop_back(); + else + set(m_size, 0); + } + operator std::string() const { + std::string str(m_size, '0'); + for (size_type i = 0; i != m_size; i++) str[i] = '0' + get(m_size - 1 - i); + return str; + } + operator uint64_t() const { return m_str[0]; } + }; + template <> struct StaticIntegerString<10> { std::string m_str; static constexpr uint32_t base() { return 10; } @@ -88,6 +136,16 @@ namespace OY { } }; uint32_t DynamicIntegerString::s_base; + template + Ostream &operator<<(Ostream &out, const StaticIntegerString &str) { + for (size_type i = str.size() - 1; ~i; i--) out << str.get(i); + return out; + } + template + Ostream &operator<<(Ostream &out, const DynamicIntegerString &str) { + for (size_type i = str.size() - 1; ~i; i--) out << str.get(i); + return out; + } template IntStr prev_number(IntStr s) { size_type n = s.size(), i = 0; @@ -299,7 +357,10 @@ namespace OY { if (this->m_from[len - 1][state].size() && mapping(state, len)) base_type::enumerate2(state, len, call); } }; + using IntStr2 = StaticIntegerString<2>; using IntStr10 = StaticIntegerString<10>; + IntStr2 prev_number_base2(std::string s) { return prev_number(IntStr2(std::move(s))); } + IntStr2 next_number_base2(std::string s) { return next_number(IntStr2(std::move(s))); } IntStr10 prev_number_base10(std::string s) { return prev_number(IntStr10(std::move(s))); } IntStr10 next_number_base10(std::string s) { return next_number(IntStr10(std::move(s))); } } diff --git a/MISC/DigitDP.md b/MISC/DigitDP.md index 6100359..42c4296 100644 --- a/MISC/DigitDP.md +++ b/MISC/DigitDP.md @@ -20,15 +20,16 @@ 14. [2827. 范围中美丽整数的数目](https://leetcode.cn/problems/number-of-beautiful-integers-in-the-range/) 15. [2999. 统计强大整数的数目](https://leetcode.cn/problems/count-the-number-of-powerful-integers/) 16. [3007. 价值和小于等于 K 的最大数字](https://leetcode.cn/problems/maximum-number-that-sum-of-the-prices-is-less-than-or-equal-to-k/) -17. [#10165. 「一本通 5.3 例 3」Windy 数](https://loj.ac/p/10165) -18. [P2602 [ZJOI2010] 数字计数](https://www.luogu.com.cn/problem/P2602) -19. [P2657 [SCOI2009] windy 数](https://www.luogu.com.cn/problem/P2657) -20. [P3311 [SDOI2014] 数数](https://www.luogu.com.cn/problem/P3311) -21. [P3413 SAC#1 - 萌数](https://www.luogu.com.cn/problem/P3413) -22. [P4127 [AHOI2009] 同类分布](https://www.luogu.com.cn/problem/P4127) -23. [P4317 花神的数论题](https://www.luogu.com.cn/problem/P4317) -24. [P8764 [蓝桥杯 2021 国 BC] 二进制问题](https://www.luogu.com.cn/problem/P8764) -25. [小苯的数位MEX](https://ac.nowcoder.com/acm/problem/280854) +17. [3352. 统计小于 N 的 K 可约简整数](https://leetcode.cn/problems/count-k-reducible-numbers-less-than-n/) +18. [#10165. 「一本通 5.3 例 3」Windy 数](https://loj.ac/p/10165) +19. [P2602 [ZJOI2010] 数字计数](https://www.luogu.com.cn/problem/P2602) +20. [P2657 [SCOI2009] windy 数](https://www.luogu.com.cn/problem/P2657) +21. [P3311 [SDOI2014] 数数](https://www.luogu.com.cn/problem/P3311) +22. [P3413 SAC#1 - 萌数](https://www.luogu.com.cn/problem/P3413) +23. [P4127 [AHOI2009] 同类分布](https://www.luogu.com.cn/problem/P4127) +24. [P4317 花神的数论题](https://www.luogu.com.cn/problem/P4317) +25. [P8764 [蓝桥杯 2021 国 BC] 二进制问题](https://www.luogu.com.cn/problem/P8764) +26. [小苯的数位MEX](https://ac.nowcoder.com/acm/problem/280854) ### 二、模板功能 diff --git a/README.md b/README.md index 1cefae2..717062d 100644 --- a/README.md +++ b/README.md @@ -111,3 +111,12 @@ 6. 用 `make_SegTree` 可以创建一颗线段树;但是如果我要在 `std::vector` 里存放十颗线段树,我还是得把类型全称写出来,可是我写不出来,怎么办? 既然用 `make_SegTree` 可以创建出一颗线段树,那么可以用 `using NickName = decltype(make_SegTree<...>(...));` 来捕获这棵树的类型,并给它起个别名。接下来即可用 `std::vector` 的方式存储十颗线段树。 + +7. 为什么使用 `StaticModInt64` , `DynamicModInt64` 取模结果出错? + + 本模板库要求 `gcc` 或者 `clang` 编译器的 `long double` 具有 `80` 个 `bit` 的 `size` ;可以通过 `std::numeric_limits::max()` 检查输出是否为 `pow(10, 4932)` 以上。如果不够,那么基于 `long double` 进行的计算就可能因精度不足而出错。 + + 一般而言, `MSVC` 编译器的 `long double` 只有 `64` 个 `bit` 的 `size` ,所以在 `MSVC` 环境里往往不使用 `long double` 进行计算,而是通过其他算法进行计算。所以, `MSVC` 编译器的 `long double` 位数不足不会导致计算结果错误。 + + + \ No newline at end of file diff --git a/TEST/local/BellmanFord_test.cpp b/TEST/local/BellmanFord_test.cpp index fc6c783..c2001cd 100644 --- a/TEST/local/BellmanFord_test.cpp +++ b/TEST/local/BellmanFord_test.cpp @@ -32,10 +32,6 @@ void test_distance_sum() { // 如果模板参数为 true,那么查询器还可以查询最短路的结点编号 using group = OY::BellmanFord::AddGroup; - // 第一个参数表示距离求和 - // 第二个参数表示不计数 - // 第三个参数表示求最小距离 - // 第四个参数表示要保存路径 auto table2 = G.calc(0).first; table2.trace( diff --git a/TEST/local/DijkstraKPath_test.cpp b/TEST/local/DijkstraKPath_test.cpp new file mode 100644 index 0000000..49323f1 --- /dev/null +++ b/TEST/local/DijkstraKPath_test.cpp @@ -0,0 +1,131 @@ +#include "GRAPH/DijkstraKPath.h" +#include "IO/FastIO.h" +#include "TEST/std_bit.h" + +void test_distance_sum() { + // 普通使用者只需要了解熟悉 OY::Dijkstra::Graph 的使用 + cout << "test distance sum:\n"; + + // 建图 + OY::DijkstraKPath::Graph G(7, 10); + // 注意加的边都是有向边 + G.add_edge(0, 1, 100); + G.add_edge(0, 2, 200); + G.add_edge(2, 0, 1); + G.add_edge(3, 4, 100); + G.add_edge(3, 5, 100); + G.add_edge(0, 3, 95); + G.add_edge(6, 4, 100); + G.add_edge(4, 5, 190); + G.add_edge(5, 1, 100); + G.add_edge(5, 6, 200); + + // 获取最短路,次短路长度查询器 + auto table = G.calc<2>(0); + cout << "top2 dis from 0 to 0:" << table.query(0, 0) << ',' << table.query(0, 1) << endl; + cout << "top2 dis from 0 to 2:" << table.query(2, 0) << ',' << table.query(2, 1) << endl; + cout << "top2 dis from 0 to 6:" << table.query(6, 0) << ',' << table.query(6, 1) << endl; + + // 如果模板参数为 true,那么查询器还可以查询次短路的结点编号 + using group = OY::DijkstraKPath::AddGroup; + + auto table2 = G.calc<2, group, void, true>(0); + // 最短路路径 + cout << "No.1 path:\n"; + table2.trace(6, 0, [](int from, int to) { cout << "go from " << from << " -> " << to << endl; }); + cout << "No.2 path:\n"; + table2.trace(6, 1, [](int from, int to) { cout << "go from " << from << " -> " << to << endl; }); + + // G 本身有更方便的接口 + std::vector path1 = G.get_path(0, 6, 0); + for (int i = 0; i < path1.size(); i++) cout << path1[i] << (i + 1 == path1.size() ? "\n" : " -> "); + std::vector path2 = G.get_path(0, 6, 1); + for (int i = 0; i < path2.size(); i++) cout << path2[i] << (i + 1 == path2.size() ? "\n\n" : " -> "); +} + +void test_distance_max() { + cout << "test distance max:\n"; + + OY::DijkstraKPath::Graph G(7, 10); + G.add_edge(0, 1, 100); + G.add_edge(0, 2, 200); + G.add_edge(2, 0, 1); + G.add_edge(3, 4, 100); + G.add_edge(3, 5, 100); + G.add_edge(0, 3, 95); + G.add_edge(6, 4, 100); + G.add_edge(4, 5, 190); + G.add_edge(5, 1, 100); + G.add_edge(5, 6, 200); + + // 定义路径长度为路径中的边长的最大值 + // 获取最短路查询器 + using group = OY::DijkstraKPath::MaxGroup; + auto table = G.calc<2, group>(0); + cout << "top2 dis from 0 to 0:" << table.query(0, 0) << ',' << table.query(0, 1) << endl; + cout << "top2 dis from 0 to 2:" << table.query(2, 0) << ',' << table.query(2, 1) << endl; + cout << "top2 dis from 0 to 6:" << table.query(6, 0) << ',' << table.query(6, 1) << endl; + cout << endl; +} + +void test_count() { + cout << "test path count:\n"; + + OY::DijkstraKPath::Graph G(4, 7); + G.add_edge(0, 3, 100); + G.add_edge(0, 2, 200); + G.add_edge(0, 1, 120); + G.add_edge(1, 2, 80); + G.add_edge(2, 3, 300); + G.add_edge(2, 3, 300); + G.add_edge(2, 0, 1); + + // 获取最短路路径数查询器 + using monoid = OY::DijkstraKPath::AddGroup; + auto table = G.calc<3, monoid, int>(0); + cout << "No.1 dis from 0 to 3:" << table.query(3, 0) << endl; + cout << "path count:" << table.query_count(3, 0) << endl; + cout << "No.2 dis from 0 to 3:" << table.query(3, 1) << endl; + cout << "path count:" << table.query_count(3, 1) << endl; + cout << "No.3 dis from 0 to 3:" << table.query(3, 2) << endl; + cout << "path count:" << table.query_count(3, 2) << endl; + cout << endl; +} + +int main() { + test_distance_sum(); + test_distance_max(); + test_count(); +} +/* +#输出如下 +test distance sum: +top2 dis from 0 to 0:0,201 +top2 dis from 0 to 2:200,401 +top2 dis from 0 to 6:395,585 +No.1 path: +go from 0 -> 3 +go from 3 -> 5 +go from 5 -> 6 +No.2 path: +go from 0 -> 3 +go from 3 -> 4 +go from 4 -> 5 +go from 5 -> 6 +0 -> 3 -> 5 -> 6 +0 -> 3 -> 4 -> 5 -> 6 + +test distance max: +top2 dis from 0 to 0:0,200 +top2 dis from 0 to 2:200,200 +top2 dis from 0 to 6:200,1073741823 + +test path count: +No.1 dis from 0 to 3:100 +path count:1 +No.2 dis from 0 to 3:301 +path count:2 +No.3 dis from 0 to 3:500 +path count:4 + +*/ \ No newline at end of file diff --git a/TEST/local/Dijkstra_naive_test.cpp b/TEST/local/Dijkstra_naive_test.cpp index b010e93..5233be2 100644 --- a/TEST/local/Dijkstra_naive_test.cpp +++ b/TEST/local/Dijkstra_naive_test.cpp @@ -7,7 +7,7 @@ void test_distance_sum() { cout << "test distance sum:\n"; // 建图 - OY::DijkstraNaive::Graph G(7, 9); + OY::VectorAddDijkstraNaive G(7); // 注意加的边都是有向边 G.add_edge(0, 1, 100); G.add_edge(0, 2, 200); @@ -26,13 +26,8 @@ void test_distance_sum() { cout << "min dis from 0 to 6:" << table.query(6) << endl; // 如果模板参数为 true,那么查询器还可以查询最短路的结点编号 - using group = OY::DijkstraNaive::AddGroup; - // 第一个参数表示距离求和 - // 第二个参数表示不计数 - // 第三个参数表示求最小距离 - // 第四个参数表示要保存路径 - auto table2 = G.calc(0); + auto table2 = G.calc(0); table2.trace(6, [](int from, int to) { cout << "go from " << from << " -> " << to << endl; }); // G 本身有更方便的接口 @@ -43,7 +38,7 @@ void test_distance_sum() { void test_distance_max() { cout << "test distance max:\n"; - OY::DijkstraNaive::Graph G(7, 9); + OY::VectorMaxDijkstraNaive G(7); G.add_edge(0, 1, 100); G.add_edge(0, 2, 200); G.add_edge(3, 4, 100); @@ -56,8 +51,7 @@ void test_distance_max() { // 定义路径长度为路径中的边长的最大值 // 获取最短路查询器 - using group = OY::DijkstraNaive::MaxGroup; - auto table = G.calc(0); + auto table = G.calc(0); cout << "min dis from 0 to 0:" << table.query(0) << endl; cout << "min dis from 0 to 2:" << table.query(2) << endl; cout << "min dis from 0 to 6:" << table.query(6) << endl; @@ -67,7 +61,7 @@ void test_distance_max() { void test_count() { cout << "test path count:\n"; - OY::DijkstraNaive::Graph G(4, 5); + OY::VectorAddDijkstraNaive G(4); G.add_edge(0, 1, 100); G.add_edge(1, 2, 200); G.add_edge(2, 3, 100); @@ -75,54 +69,16 @@ void test_count() { G.add_edge(1, 3, 300); // 获取最短路路径数查询器 - using monoid = OY::DijkstraNaive::AddGroup; - auto table = G.calc(0); + auto table = G.calc(0); cout << "min dis from 0 to 3:" << table.query(3) << endl; cout << "path count:" << table.query_count(3) << endl; cout << endl; } -void test_solver() { -#if CPP_STANDARD >= 201402L - // 进阶使用者,可以把 Solver 用到自己的图里 - cout << "test solver:\n"; - // 这里以常见的二维 vector 存图举例 - std::vector>> adj(7); - adj[0].push_back({1, 100}); - adj[0].push_back({2, 200}); - adj[3].push_back({4, 100}); - adj[3].push_back({5, 100}); - adj[0].push_back({3, 95}); - adj[6].push_back({4, 100}); - adj[4].push_back({5, 190}); - adj[5].push_back({1, 100}); - adj[5].push_back({6, 200}); - - // 直接建一个可追溯最短路的解答器 - using group = OY::DijkstraNaive::AddGroup; - OY::DijkstraNaive::Solver sol(7); - sol.set_distance(0, 0); - // 传递一个遍历边的泛型回调 - sol.run(-1, [&](int from, auto call) { - for (auto to_and_dis : adj[from]) call(to_and_dis.first, to_and_dis.second); - }); - - // 查询最短路长度 - cout << "min dis from 0 to 0:" << sol.query(0) << endl; - cout << "min dis from 0 to 2:" << sol.query(2) << endl; - cout << "min dis from 0 to 6:" << sol.query(6) << endl; - - // 生成一个最短路径 - sol.trace(6, [](int from, int to) { cout << "from " << from << " to " << to << endl; }); - -#endif -} - int main() { test_distance_sum(); test_distance_max(); test_count(); - test_solver(); } /* #输出如下 @@ -144,12 +100,4 @@ test path count: min dis from 0 to 3:400 path count:3 -test solver: -min dis from 0 to 0:0 -min dis from 0 to 2:200 -min dis from 0 to 6:395 -from 0 to 3 -from 3 to 5 -from 5 to 6 - */ \ No newline at end of file diff --git a/TEST/local/Dijkstra_heap_test.cpp b/TEST/local/Dijkstra_test.cpp similarity index 85% rename from TEST/local/Dijkstra_heap_test.cpp rename to TEST/local/Dijkstra_test.cpp index 89ed0f4..dd38d17 100644 --- a/TEST/local/Dijkstra_heap_test.cpp +++ b/TEST/local/Dijkstra_test.cpp @@ -1,13 +1,13 @@ -#include "GRAPH/Dijkstra_heap.h" +#include "GRAPH/Dijkstra.h" #include "IO/FastIO.h" #include "TEST/std_bit.h" void test_distance_sum() { - // 普通使用者只需要了解熟悉 OY::DijkstraHeap::Graph 的使用 + // 普通使用者只需要了解熟悉 OY::Dijkstra::Graph 的使用 cout << "test distance sum:\n"; // 建图 - OY::DijkstraHeap::Graph G(7, 9); + OY::Dijkstra::Graph G(7, 9); // 注意加的边都是有向边 G.add_edge(0, 1, 100); G.add_edge(0, 2, 200); @@ -26,11 +26,7 @@ void test_distance_sum() { cout << "min dis from 0 to 6:" << table.query(6) << endl; // 如果模板参数为 true,那么查询器还可以查询最短路的结点编号 - using group = OY::DijkstraHeap::AddGroup; - // 第一个参数表示距离求和 - // 第二个参数表示不计数 - // 第三个参数表示求最小距离 - // 第四个参数表示要保存路径 + using group = OY::Dijkstra::AddGroup; auto table2 = G.calc(0); table2.trace(6, [](int from, int to) { cout << "go from " << from << " -> " << to << endl; }); @@ -43,7 +39,7 @@ void test_distance_sum() { void test_distance_max() { cout << "test distance max:\n"; - OY::DijkstraHeap::Graph G(7, 9); + OY::Dijkstra::Graph G(7, 9); G.add_edge(0, 1, 100); G.add_edge(0, 2, 200); G.add_edge(3, 4, 100); @@ -56,7 +52,7 @@ void test_distance_max() { // 定义路径长度为路径中的边长的最大值 // 获取最短路查询器 - using group = OY::DijkstraHeap::MaxGroup; + using group = OY::Dijkstra::MaxGroup; auto table = G.calc(0); cout << "min dis from 0 to 0:" << table.query(0) << endl; cout << "min dis from 0 to 2:" << table.query(2) << endl; @@ -67,7 +63,7 @@ void test_distance_max() { void test_count() { cout << "test path count:\n"; - OY::DijkstraHeap::Graph G(4, 5); + OY::Dijkstra::Graph G(4, 5); G.add_edge(0, 1, 100); G.add_edge(1, 2, 200); G.add_edge(2, 3, 100); @@ -75,7 +71,7 @@ void test_count() { G.add_edge(1, 3, 300); // 获取最短路路径数查询器 - using monoid = OY::DijkstraHeap::AddGroup; + using monoid = OY::Dijkstra::AddGroup; auto table = G.calc(0); cout << "min dis from 0 to 3:" << table.query(3) << endl; cout << "path count:" << table.query_count(3) << endl; @@ -99,8 +95,8 @@ void test_solver() { adj[5].push_back({6, 200}); // 直接建一个可追溯最短路的解答器 - using group = OY::DijkstraHeap::AddGroup; - OY::DijkstraHeap::Solver sol(7); + using group = OY::Dijkstra::AddGroup; + OY::Dijkstra::Solver sol(7); sol.set_distance(0, 0); // 传递一个遍历边的泛型回调 sol.run(-1, [&](int from, auto call) { diff --git a/TEST/local/SPFAKPath_test.cpp b/TEST/local/SPFAKPath_test.cpp new file mode 100644 index 0000000..1dda7c6 --- /dev/null +++ b/TEST/local/SPFAKPath_test.cpp @@ -0,0 +1,132 @@ +#include "GRAPH/DijkstraKPath.h" +#include "GRAPH/SPFAKPath.h" +#include "IO/FastIO.h" +#include "TEST/std_bit.h" + +void test_distance_sum() { + // 普通使用者只需要了解熟悉 OY::SPFAKPath::Graph 的使用 + cout << "test distance sum:\n"; + + // 建图 + OY::SPFAKPath::Graph G(7, 10); + // 注意加的边都是有向边 + G.add_edge(0, 1, 100); + G.add_edge(0, 2, 200); + G.add_edge(2, 0, 1); + G.add_edge(3, 4, 100); + G.add_edge(3, 5, 100); + G.add_edge(0, 3, 95); + G.add_edge(6, 4, 100); + G.add_edge(4, 5, 190); + G.add_edge(5, 1, 100); + G.add_edge(5, 6, 200); + + // 获取最短路,次短路长度查询器 + auto table = G.calc<2>(0).first; + cout << "top2 dis from 0 to 0:" << table.query(0, 0) << ',' << table.query(0, 1) << endl; + cout << "top2 dis from 0 to 2:" << table.query(2, 0) << ',' << table.query(2, 1) << endl; + cout << "top2 dis from 0 to 6:" << table.query(6, 0) << ',' << table.query(6, 1) << endl; + + // 如果模板参数为 true,那么查询器还可以查询次短路的结点编号 + using group = OY::SPFAKPath::AddGroup; + + auto table2 = G.calc<2, group, void, true>(0).first; + // 最短路路径 + cout << "No.1 path:\n"; + table2.trace(6, 0, [](int from, int to) { cout << "go from " << from << " -> " << to << endl; }); + cout << "No.2 path:\n"; + table2.trace(6, 1, [](int from, int to) { cout << "go from " << from << " -> " << to << endl; }); + + // G 本身有更方便的接口 + std::vector path1 = G.get_path(0, 6, 0); + for (int i = 0; i < path1.size(); i++) cout << path1[i] << (i + 1 == path1.size() ? "\n" : " -> "); + std::vector path2 = G.get_path(0, 6, 1); + for (int i = 0; i < path2.size(); i++) cout << path2[i] << (i + 1 == path2.size() ? "\n\n" : " -> "); +} + +void test_distance_max() { + cout << "test distance max:\n"; + + OY::SPFAKPath::Graph G(7, 10); + G.add_edge(0, 1, 100); + G.add_edge(0, 2, 200); + G.add_edge(2, 0, 1); + G.add_edge(3, 4, 100); + G.add_edge(3, 5, 100); + G.add_edge(0, 3, 95); + G.add_edge(6, 4, 100); + G.add_edge(4, 5, 190); + G.add_edge(5, 1, 100); + G.add_edge(5, 6, 200); + + // 定义路径长度为路径中的边长的最大值 + // 获取最短路查询器 + using group = OY::SPFAKPath::MaxGroup; + auto table = G.calc<2, group>(0).first; + cout << "top2 dis from 0 to 0:" << table.query(0, 0) << ',' << table.query(0, 1) << endl; + cout << "top2 dis from 0 to 2:" << table.query(2, 0) << ',' << table.query(2, 1) << endl; + cout << "top2 dis from 0 to 6:" << table.query(6, 0) << ',' << table.query(6, 1) << endl; + cout << endl; +} + +void test_count() { + cout << "test path count:\n"; + + OY::SPFAKPath::Graph G(4, 7); + G.add_edge(0, 3, 100); + G.add_edge(0, 2, 200); + G.add_edge(0, 1, 120); + G.add_edge(1, 2, 80); + G.add_edge(2, 3, 300); + G.add_edge(2, 3, 300); + G.add_edge(2, 0, 1); + + // 获取最短路路径数查询器 + using monoid = OY::SPFAKPath::AddGroup; + auto table = G.calc<3, monoid, int>(0).first; + cout << "No.1 dis from 0 to 3:" << table.query(3, 0) << endl; + cout << "path count:" << table.query_count(3, 0) << endl; + cout << "No.2 dis from 0 to 3:" << table.query(3, 1) << endl; + cout << "path count:" << table.query_count(3, 1) << endl; + cout << "No.3 dis from 0 to 3:" << table.query(3, 2) << endl; + cout << "path count:" << table.query_count(3, 2) << endl; + cout << endl; +} + +int main() { + test_distance_sum(); + test_distance_max(); + test_count(); +} +/* +#输出如下 +test distance sum: +top2 dis from 0 to 0:0,201 +top2 dis from 0 to 2:200,401 +top2 dis from 0 to 6:395,585 +No.1 path: +go from 0 -> 3 +go from 3 -> 5 +go from 5 -> 6 +No.2 path: +go from 0 -> 3 +go from 3 -> 4 +go from 4 -> 5 +go from 5 -> 6 +0 -> 3 -> 5 -> 6 +0 -> 3 -> 4 -> 5 -> 6 + +test distance max: +top2 dis from 0 to 0:0,200 +top2 dis from 0 to 2:200,200 +top2 dis from 0 to 6:200,200 + +test path count: +No.1 dis from 0 to 3:100 +path count:1 +No.2 dis from 0 to 3:301 +path count:2 +No.3 dis from 0 to 3:500 +path count:4 + +*/ \ No newline at end of file diff --git a/TEST/local/SPFA_test.cpp b/TEST/local/SPFA_test.cpp index 9e651aa..d1fd906 100644 --- a/TEST/local/SPFA_test.cpp +++ b/TEST/local/SPFA_test.cpp @@ -32,10 +32,6 @@ void test_distance_sum() { // 如果模板参数为 true,那么查询器还可以查询最短路的结点编号 using group = OY::SPFA::AddGroup; - // 第一个参数表示距离求和 - // 第二个参数表示不计数 - // 第三个参数表示求最小距离 - // 第四个参数表示要保存路径 auto table2 = G.calc(0).first; table2.trace(6, [](int from, int to) { cout << "go from " << from << " -> " << to << endl; }); diff --git a/TEST/oj/hdoj_1596.cpp b/TEST/oj/hdoj_1596.cpp index bc92b02..5410dc7 100644 --- a/TEST/oj/hdoj_1596.cpp +++ b/TEST/oj/hdoj_1596.cpp @@ -1,6 +1,8 @@ -#include "GRAPH/Floyd.h" +#include "DS/FastHeap.h" #include "DS/LinkBucket.h" -#include "GRAPH/Dijkstra_heap.h" +#include "DS/SiftHeap.h" +#include "GRAPH/Dijkstra.h" +#include "GRAPH/Floyd.h" #include "IO/FastIO.h" /* @@ -13,7 +15,7 @@ void solve_dijk() { uint32_t n; while (cin >> n) { - OY::DijkstraHeap::Graph G(n, n * n); + OY::Dijkstra::Graph G(n, n * n); for (uint32_t i = 0; i != n; i++) { for (uint32_t j = 0; j != n; j++) { double dis; @@ -43,7 +45,8 @@ void solve_dijk() { static value_type identity() { return 1; } static value_type infinite() { return 0; } }; - auto sol = G.calc(i); + auto sol = G.calc(i); + // auto sol = G.calc(i); for (auto qi : qb[i]) ans[qi] = sol.query(qs[qi]); } for (uint32_t qi = 0; qi != q; qi++) diff --git a/TEST/oj/hdoj_3191.cpp b/TEST/oj/hdoj_3191.cpp new file mode 100644 index 0000000..e2c3180 --- /dev/null +++ b/TEST/oj/hdoj_3191.cpp @@ -0,0 +1,71 @@ +#include "GRAPH/DijkstraKPath.h" +#include "GRAPH/SPFAKPath.h" +#include "GRAPH/TopologicalSort.h" +#include "IO/FastIO.h" + +/* +[How Many Paths Are There](https://acm.hdu.edu.cn/showproblem.php?pid=3191) +*/ +/** + * 本题为严格次短路计数模板题 + * 坑点在于,有零权边 + * 我们根据零权边,做个拓扑排序,求出拓扑序 + * 排序的时候带上拓扑序比较 + */ + +struct edge { + uint32_t len, target; +}; +uint32_t topo_id[50]; +struct dissum { + uint32_t len, target; + dissum operator+(const edge &e) const { return {len + e.len, e.target}; } + bool operator<(const dissum &rhs) const { return len < rhs.len || (len == rhs.len && topo_id[target] < topo_id[rhs.target]); } +}; +constexpr dissum inf{0x3f3f3f3f}; +void solve_dijk() { + uint32_t n, m, s, t; + while (cin >> n >> m >> s >> t) { + OY::DijkstraKPath::Graph G(n, m); + for (uint32_t i = 0; i != m; i++) { + uint32_t a, b, w; + cin >> a >> b >> w; + G.add_edge(a, b, {w, b}); + } + + G._prepare(); + auto topo_sol = OY::TOPO::Solver(n); + topo_sol.run([&](uint32_t from, auto &&call) { G(from, [&](uint32_t to, const edge &e) { if(e.len)call(to); }); }); + topo_sol.trace([id = 0](auto x) mutable { topo_id[x] = id++; }); + + using monoid = OY::DijkstraKPath::AddGroup, inf>; + auto sol = G.calc<2, monoid, uint32_t>(s, t); + cout << sol.query(n - 1, 1).len << ' ' << sol.query_count(n - 1, 1) << endl; + } +} + +void solve_spfa() { + uint32_t n, m, s, t; + while (cin >> n >> m >> s >> t) { + OY::SPFAKPath::Graph G(n, m); + for (uint32_t i = 0; i != m; i++) { + uint32_t a, b, w; + cin >> a >> b >> w; + G.add_edge(a, b, {w, b}); + } + + G._prepare(); + auto topo_sol = OY::TOPO::Solver(n); + topo_sol.run([&](uint32_t from, auto &&call) { G(from, [&](uint32_t to, const edge &e) { if(e.len)call(to); }); }); + topo_sol.trace([id = 0](auto x) mutable { topo_id[x] = id++; }); + + using monoid = OY::SPFAKPath::AddGroup, inf>; + auto [sol, flag] = G.calc<2, monoid, uint32_t>(s); + cout << sol.query(n - 1, 1).len << ' ' << sol.query_count(n - 1, 1) << endl; + } +} + +int main() { + solve_dijk(); + // solve_spfa(); +} \ No newline at end of file diff --git a/TEST/oj/hdoj_3790.cpp b/TEST/oj/hdoj_3790.cpp index 3ae0925..3df2eb8 100644 --- a/TEST/oj/hdoj_3790.cpp +++ b/TEST/oj/hdoj_3790.cpp @@ -1,4 +1,4 @@ -#include "GRAPH/Dijkstra_heap.h" +#include "GRAPH/Dijkstra.h" #include "IO/FastIO.h" /* @@ -21,7 +21,7 @@ void solve_dijk() { uint32_t n, m; cin >> n >> m; if (!n) break; - OY::DijkstraHeap::Graph G(n, m); + OY::Dijkstra::Graph G(n, m); for (uint32_t i = 0; i != m; i++) { uint32_t a, b, dis, cost; cin >> a >> b >> dis >> cost; diff --git a/TEST/oj/hdoj_6026.cpp b/TEST/oj/hdoj_6026.cpp new file mode 100644 index 0000000..c4ac8f1 --- /dev/null +++ b/TEST/oj/hdoj_6026.cpp @@ -0,0 +1,92 @@ +#include "GRAPH/BellmanFord.h" +#include "GRAPH/Dijkstra.h" +#include "GRAPH/Dijkstra_naive.h" +#include "GRAPH/SPFA.h" +#include "IO/FastIO.h" + +/* +[Deleting Edges](https://acm.hdu.edu.cn/showproblem.php?pid=6026) +*/ +/** + * 本题为 Dijkstra 算法模板题 + * 由于最终生成的树,从 0 到每个结点的距离都是原来的最短距离 + * 那么可以枚举每个结点的前驱,找出可能的最短路前驱,再随便选一条 + */ + +char dis[50][50]; +void solve_dijk_naive() { + uint32_t n; + while (cin >> n) { + using monoid = OY::DijkstraNaive::AddGroup; + OY::DijkstraNaive::Graph G(n); + for (uint32_t i = 0; i != n; i++) + for (uint32_t j = 0; j != n; j++) cin >> dis[i][j]; + for (uint32_t i = 0; i != n; i++) + for (uint32_t j = 0; j != n; j++) + if (dis[i][j] != '0') G.add_edge(i, j, dis[i][j] - '0'); + + auto sol = G.calc(0); + uint32_t ans = 1; + for (uint32_t i = 1; i != n; i++) { + uint32_t cand = 0; + for (uint32_t j = 0; j != n; j++) + if (dis[i][j] != '0') + if (sol.query(j) + (dis[j][i] - '0') == sol.query(i)) cand++; + ans = 1ull * ans * cand % 1000000007; + } + cout << ans << endl; + } +} + +void solve_dijk() { + uint32_t n; + while (cin >> n) { + OY::Dijkstra::Graph G(n, n * n); + for (uint32_t i = 0; i != n; i++) + for (uint32_t j = 0; j != n; j++) cin >> dis[i][j]; + for (uint32_t i = 0; i != n; i++) + for (uint32_t j = 0; j != n; j++) + if (dis[i][j] != '0') G.add_edge(i, j, dis[i][j] - '0'); + + auto sol = G.calc(0); + uint32_t ans = 1; + for (uint32_t i = 1; i != n; i++) { + uint32_t cand = 0; + for (uint32_t j = 0; j != n; j++) + if (dis[i][j] != '0') + if (sol.query(j) + (dis[j][i] - '0') == sol.query(i)) cand++; + ans = 1ull * ans * cand % 1000000007; + } + cout << ans << endl; + } +} + +void solve_spfa() { + uint32_t n; + while (cin >> n) { + // OY::SPFA::Graph G(n, n * n); + OY::BellmanFord::Graph G(n, n * n); + for (uint32_t i = 0; i != n; i++) + for (uint32_t j = 0; j != n; j++) cin >> dis[i][j]; + for (uint32_t i = 0; i != n; i++) + for (uint32_t j = 0; j != n; j++) + if (dis[i][j] != '0') G.add_edge(i, j, dis[i][j] - '0'); + + auto [sol, flag] = G.calc(0); + uint32_t ans = 1; + for (uint32_t i = 1; i != n; i++) { + uint32_t cand = 0; + for (uint32_t j = 0; j != n; j++) + if (dis[i][j] != '0') + if (sol.query(j) + (dis[j][i] - '0') == sol.query(i)) cand++; + ans = 1ull * ans * cand % 1000000007; + } + cout << ans << endl; + } +} + +int main() { + solve_dijk_naive(); + // solve_dijk(); + // solve_spfa(); +} \ No newline at end of file diff --git a/TEST/oj/hdoj_6181.cpp b/TEST/oj/hdoj_6181.cpp new file mode 100644 index 0000000..2bade49 --- /dev/null +++ b/TEST/oj/hdoj_6181.cpp @@ -0,0 +1,86 @@ +#include "GRAPH/DijkstraKPath.h" +#include "GRAPH/SPFAKPath.h" +#include "GRAPH/KthPath.h" +#include "IO/FastIO.h" + +/* +[Two Paths HDU](https://acm.hdu.edu.cn/showproblem.php?pid=6181) +*/ +/** + * 本题主要是搞清楚题意,要找非严格次短路 + */ + +void solve_dijk() { + uint32_t t; + cin >> t; + while (t--) { + uint32_t n, m; + cin >> n >> m; + OY::DijkstraKPath::Graph G(n, m * 2); + for (uint32_t i = 0; i != m; i++) { + uint32_t a, b, w; + cin >> a >> b >> w; + G.add_edge(a - 1, b - 1, w); + G.add_edge(b - 1, a - 1, w); + } + using monoid = OY::DijkstraKPath::AddGroup; + // 注意,理论上 uint32_t 很容易被卡,可以换个非自然溢出的模数 + // 或者写个类,当到达 2 以后就不再增大 + // 但是数据很弱 + auto sol = G.calc<2, monoid, uint32_t>(0, n - 1); + if (sol.query_count(n - 1, 0) > 1) + cout << sol.query(n - 1, 0) << endl; + else + cout << sol.query(n - 1, 1) << endl; + } +} + +void solve_spfa() { + uint32_t t; + cin >> t; + while (t--) { + uint32_t n, m; + cin >> n >> m; + OY::SPFAKPath::Graph G(n, m * 2); + for (uint32_t i = 0; i != m; i++) { + uint32_t a, b, w; + cin >> a >> b >> w; + G.add_edge(a - 1, b - 1, w); + G.add_edge(b - 1, a - 1, w); + } + using monoid = OY::SPFAKPath::AddGroup; + // 注意,理论上 uint32_t 很容易被卡,可以换个非自然溢出的模数 + // 或者写个类,当到达 2 以后就不再增大 + // 但是数据很弱 + auto [sol, flag] = G.calc<2, monoid, uint32_t>(0); + if (sol.query_count(n - 1, 0) > 1) + cout << sol.query(n - 1, 0) << endl; + else + cout << sol.query(n - 1, 1) << endl; + } +} + +void solve_kpath() { + uint32_t t; + cin >> t; + while (t--) { + uint32_t n, m; + cin >> n >> m; + OY::KPATH::Graph G(n, m * 2); + for (uint32_t i = 0; i != m; i++) { + uint32_t a, b, w; + cin >> a >> b >> w; + G.add_edge(a - 1, b - 1, w); + G.add_edge(b - 1, a - 1, w); + } + G.calc(0, n - 1); + G.next(); + cout << G.next() << endl; + } +} + +int main() { + solve_dijk(); + // solve_spfa(); + // solve_kpath(); +} \ No newline at end of file diff --git a/TEST/oj/lc_1631.cpp b/TEST/oj/lc_1631.cpp index 343fb14..346cf76 100644 --- a/TEST/oj/lc_1631.cpp +++ b/TEST/oj/lc_1631.cpp @@ -1,4 +1,4 @@ -#include "GRAPH/Dijkstra_heap.h" +#include "GRAPH/Dijkstra.h" #include "IO/LeetcodeIO.h" using namespace std; @@ -13,7 +13,7 @@ class Solution { int solve_dijk(vector> &heights) { int m = heights.size(); int n = heights[0].size(); - OY::DijkstraHeap::Graph G(m * n, (m - 1) * (n - 1)); + OY::Dijkstra::Graph G(m * n, (m - 1) * (n - 1)); static constexpr std::array, 4> dirs{{{0, 1}, {1, 0}, {0, -1}, {-1, 0}}}; for (int i = 0; i < m; i++) for (int j = 0; j < n; j++) { @@ -24,7 +24,7 @@ class Solution { } } } - auto sol = G.calc>(0, m * n - 1); + auto sol = G.calc>(0, m * n - 1); return sol.query(m * n - 1); } diff --git a/TEST/oj/lc_2045.cpp b/TEST/oj/lc_2045.cpp new file mode 100644 index 0000000..266daa5 --- /dev/null +++ b/TEST/oj/lc_2045.cpp @@ -0,0 +1,71 @@ +#include "GRAPH/DijkstraKPath.h" +#include "GRAPH/SPFAKPath.h" +#include "IO/LeetcodeIO.h" +using namespace std; + +/* +[2045. 到达目的地的第二短时间](https://leetcode.cn/problems/second-minimum-time-to-reach-destination/) +*/ +/** + * 本题求严格次短路 + */ + +class Solution { + int solve_dijk(int n, vector> &edges, int time, int change) { + OY::DijkstraKPath::Graph G(n, edges.size() * 2); + for (auto &e : edges) G.add_edge(e[0] - 1, e[1] - 1, {}), G.add_edge(e[1] - 1, e[0] - 1, {}); + + static int Time, Change; + Time = time, Change = change; + struct monoid { + using value_type = bool; + using sum_type = int; + using compare_type = less; + static sum_type op(const sum_type &x, const value_type &y) { + auto q = x / (Change * 2), r = x - Change * 2 * q; + return r < Change ? x + Time : (q + 1) * Change * 2 + Time; + } + static sum_type identity() { return {}; } + static sum_type infinite() { return 0x3f3f3f3f; } + }; + auto sol = G.calc<2, monoid>(0, n - 1); + return sol.query(n - 1, 1); + } + int solve_spfa(int n, vector> &edges, int time, int change) { + OY::SPFAKPath::Graph G(n, edges.size() * 2); + for (auto &e : edges) G.add_edge(e[0] - 1, e[1] - 1, {}), G.add_edge(e[1] - 1, e[0] - 1, {}); + + static int Time, Change; + Time = time, Change = change; + struct monoid { + using value_type = bool; + using sum_type = int; + using compare_type = less; + static sum_type op(const sum_type &x, const value_type &y) { + auto q = x / (Change * 2), r = x - Change * 2 * q; + return r < Change ? x + Time : (q + 1) * Change * 2 + Time; + } + static sum_type identity() { return {}; } + static sum_type infinite() { return 0x3f3f3f3f; } + }; + auto [sol, flag] = G.calc<2, monoid>(0); + return sol.query(n - 1, 1); + } + +public: + int secondMinimum(int n, vector> &edges, int time, int change) { + return solve_spfa(n, edges, time, change); + // return solve_dijk(n, edges, time, change); + } +}; + +#ifdef OY_LOCAL +int main() { + REGISTER_CONSTRUCTOR_SOLUTION; + REGISTER_MEMBERFUNCTION_SOLUTION(secondMinimum); + while (true) { + executor.constructSolution(); + executor.executeSolution(); + } +} +#endif \ No newline at end of file diff --git a/TEST/oj/lc_3123.cpp b/TEST/oj/lc_3123.cpp index 1dcd395..abd9f7b 100644 --- a/TEST/oj/lc_3123.cpp +++ b/TEST/oj/lc_3123.cpp @@ -1,4 +1,4 @@ -#include "GRAPH/Dijkstra_heap.h" +#include "GRAPH/Dijkstra.h" #include "GRAPH/SPFA.h" #include "IO/LeetcodeIO.h" using namespace std; @@ -13,7 +13,7 @@ using namespace std; class Solution { vector solve_dijk(int n, vector> &edges) { - OY::DijkstraHeap::Graph G(n, edges.size() * 2); + OY::Dijkstra::Graph G(n, edges.size() * 2); for (auto &e : edges) { int a = e[0], b = e[1], c = e[2]; G.add_edge(a, b, c); diff --git a/TEST/oj/lc_3352.cpp b/TEST/oj/lc_3352.cpp new file mode 100644 index 0000000..7414297 --- /dev/null +++ b/TEST/oj/lc_3352.cpp @@ -0,0 +1,56 @@ +#include "IO/LeetcodeIO.h" +#include "MATH/StaticModInt32.h" +#include "MISC/DigitDP.h" +using namespace std; + +/* +[3352. 统计小于 N 的 K 可约简整数](https://leetcode.cn/problems/count-k-reducible-numbers-less-than-n/) +*/ +/** + * 数位 dp 模板 + * 在二进制下,对置位 1 数量有要求 + */ + +int become[5][801]; +auto global_init = [] { + for (int i = 0; i <= 800; i++) { + become[0][i] = i; + for (int j = 1; j < 5; j++) become[j][i] = __builtin_popcount(become[j - 1][i]); + } + return 0; +}(); +class Solution { +public: + int countKReducibleNumbers(string s, int k) { + using OY::DIGITDP::IntStr2; + using mint = OY::mint1000000007; + auto solve = [&](IntStr2 n) -> mint { + // 求 [1, n] 里满足要求的数字的个数 + // 单次复杂度 O(2 * 800 * 800) + auto transfer = [&](auto old, auto len, auto c) -> uint32_t { + if (!~old) old = 0; + return old + c; + }; + // 状态对应的权值 + auto map = [&](auto state, auto len) { + return become[k - 1][state] == 1; + }; + static OY::DIGITDP::AppendLowSolver sol; + // static OY::DIGITDP::AppendHighSolver sol; + auto res = sol.solve(n, s.size() + 1, transfer, map); + return res; + }; + return solve(OY::DIGITDP::prev_number_base2(s)).val(); + } +}; + +#ifdef OY_LOCAL +int main() { + REGISTER_CONSTRUCTOR_SOLUTION; + REGISTER_MEMBERFUNCTION_SOLUTION(countKReducibleNumbers); + while (true) { + executor.constructSolution(); + executor.executeSolution(); + } +} +#endif \ No newline at end of file diff --git a/TEST/oj/luogu_p1491.cpp b/TEST/oj/luogu_p1491.cpp new file mode 100644 index 0000000..904f753 --- /dev/null +++ b/TEST/oj/luogu_p1491.cpp @@ -0,0 +1,55 @@ +#include "GRAPH/Dijkstra.h" +#include "IO/FastIO.h" + +/* +[P1491 集合位置](https://www.luogu.com.cn/problem/P1491) +*/ +/** + * 本题主要是搞清楚题意,要找非严格次短路 + * 本题不能套 k 短路板子 + * 因为:题目说不许重复走一条边。而且题目还有边权为 0 的自环 + * 必须跑删边最短路 + */ + +void solve_dijk() { + uint32_t n, m; + cin >> n >> m; + std::vector> co(n); + for (auto &[x, y] : co) cin >> x >> y; + OY::Dijkstra::Graph G(n, m * 2); + for (uint32_t i = 0; i != m; i++) { + uint32_t a, b; + cin >> a >> b; + auto dis = sqrt((co[a - 1].first - co[b - 1].first) * (co[a - 1].first - co[b - 1].first) + (co[a - 1].second - co[b - 1].second) * (co[a - 1].second - co[b - 1].second)); + G.add_edge(a - 1, b - 1, dis); + G.add_edge(b - 1, a - 1, dis); + } + using monoid = OY::Dijkstra::AddGroup; + auto path = G.get_path(0, n - 1); + // 现在把这条最短路的每条边删除 + double second = 1e18; + for (uint32_t i = 0; i + 1 != path.size(); i++) { + auto ex = path[i], ey = path[i + 1]; + using monoid = OY::Dijkstra::AddGroup; + OY::Dijkstra::Solver sol(n); + sol.set_distance(0, 0); + sol.run(n - 1, [&](uint32_t from, auto &&call) { + G(from, [&](uint32_t to, double dis) { + if (from != ex || to != ey) call(to, dis); + }); + }); + second = std::min(second, sol.query(n - 1)); + } + auto write_double = [](double x) { + x += 0.005; + uint64_t x0 = x, x1 = (x - x0) * 100; + cout << x0 << '.'; + auto str = std::to_string(x1); + cout << std::string(2 - str.size(), '0') + str << endl; + }; + write_double(second); +} + +int main() { + solve_dijk(); +} \ No newline at end of file diff --git a/TEST/oj/luogu_p1576.cpp b/TEST/oj/luogu_p1576.cpp index 6c6f385..bb1a415 100644 --- a/TEST/oj/luogu_p1576.cpp +++ b/TEST/oj/luogu_p1576.cpp @@ -1,4 +1,4 @@ -#include "GRAPH/Dijkstra_heap.h" +#include "GRAPH/Dijkstra.h" #include "GRAPH/Dijkstra_naive.h" #include "GRAPH/SPFA.h" #include "IO/FastIO.h" @@ -13,8 +13,7 @@ void solve_dijk() { uint32_t n, m; cin >> n >> m; - OY::DijkstraHeap::Graph G(n, m * 2); - // OY::DijkstraNaive::Graph G(n, m * 2); + OY::Dijkstra::Graph G(n, m * 2); for (uint32_t i = 0; i != m; i++) { uint32_t x, y, z; cin >> x >> y >> z; @@ -76,7 +75,43 @@ void solve_spfa() { write_double(100. / sol.query(t - 1)); } +void solve_dijk_naive() { + uint32_t n, m; + cin >> n >> m; + struct monoid { + using value_type = double; + using sum_type = double; + using compare_type = std::greater<>; + static sum_type op(const sum_type &x, const value_type &y) { return x * y; } + static value_type value_identity() { return 1; } + static value_type value_infinite() { return 0; } + static sum_type sum_identity() { return 1; } + static sum_type sum_infinite() { return 0; } + }; + OY::DijkstraNaive::Graph G(n); + for (uint32_t i = 0; i != m; i++) { + uint32_t x, y, z; + cin >> x >> y >> z; + G.add_edge(x - 1, y - 1, (100 - z) / 100.); + G.add_edge(y - 1, x - 1, (100 - z) / 100.); + } + + uint32_t s, t; + cin >> s >> t; + auto sol = G.calc(s - 1, t - 1); + + auto write_double = [](double x) { + x += 0.000000005; + uint64_t x0 = x, x1 = (x - x0) * 100000000; + cout << x0 << '.'; + auto str = std::to_string(x1); + cout << std::string(8 - str.size(), '0') + str; + }; + write_double(100. / sol.query(t - 1)); +} + int main() { solve_dijk(); // solve_spfa(); + // solve_dijk_naive(); } \ No newline at end of file diff --git a/TEST/oj/luogu_p1608.cpp b/TEST/oj/luogu_p1608.cpp index 6474eb0..37dad53 100644 --- a/TEST/oj/luogu_p1608.cpp +++ b/TEST/oj/luogu_p1608.cpp @@ -1,5 +1,5 @@ #include "GRAPH/BellmanFord.h" -#include "GRAPH/Dijkstra_heap.h" +#include "GRAPH/Dijkstra.h" #include "GRAPH/Dijkstra_naive.h" #include "GRAPH/SPFA.h" #include "IO/FastIO.h" @@ -12,6 +12,24 @@ * 需要计数 */ +void solve_dijk_naive() { + uint32_t n, m; + cin >> n >> m; + + OY::StaticAddDijkstraNaive, 0x7fffffff, 0x7fffffff, 2000> G(n); + for (uint32_t i = 0; i < m; i++) { + uint32_t a, b, dis; + cin >> a >> b >> dis; + G.add_edge(a - 1, b - 1, dis); + } + + auto sol = G.calc(0); + if (sol.query_count(n - 1)) + cout << sol.query(n - 1) << ' ' << sol.query_count(n - 1); + else + cout << "No answer"; +} + uint32_t g[2000][2000]; auto update = [](auto &x, auto y) { if (!x) @@ -35,8 +53,8 @@ void solve_spfa() { if (g[i][j]) G.add_edge(i, j, g[i][j]); - using monoid = OY::SPFA::AddSemiGroup; - // using monoid = OY::BellmanFord::AddSemiGroup; + using monoid = OY::SPFA::AddGroup; + // using monoid = OY::BellmanFord::AddGroup; auto [sol, flag] = G.calc(0); if (sol.query_count(n - 1)) @@ -54,15 +72,13 @@ void solve_dijk() { update(g[a - 1][b - 1], dis); } - OY::DijkstraHeap::Graph G(n, std::min(n * n, m)); - // OY::DijkstraNaive::Graph G(n, std::min(n * n, m)); + OY::Dijkstra::Graph G(n, std::min(n * n, m)); for (uint32_t i = 0; i < n; i++) for (uint32_t j = 0; j < n; j++) if (g[i][j]) G.add_edge(i, j, g[i][j]); - using monoid = OY::DijkstraHeap::AddGroup; - // using monoid = OY::DijkstraNaive::AddGroup; + using monoid = OY::Dijkstra::AddGroup; auto sol = G.calc(0); if (sol.query_count(n - 1)) @@ -72,6 +88,7 @@ void solve_dijk() { } int main() { - solve_spfa(); + solve_dijk_naive(); + // solve_spfa(); // solve_dijk(); } diff --git a/TEST/oj/luogu_p2047.cpp b/TEST/oj/luogu_p2047.cpp index 86468e8..bbef935 100644 --- a/TEST/oj/luogu_p2047.cpp +++ b/TEST/oj/luogu_p2047.cpp @@ -1,8 +1,8 @@ -#include "GRAPH/Floyd.h" -#include "GRAPH/Dijkstra_heap.h" +#include "GRAPH/BellmanFord.h" +#include "GRAPH/Dijkstra.h" #include "GRAPH/Dijkstra_naive.h" +#include "GRAPH/Floyd.h" #include "GRAPH/SPFA.h" -#include "GRAPH/BellmanFord.h" #include "IO/FastIO.h" /* @@ -50,20 +50,17 @@ void solve_floyd() { void solve_dijk() { uint32_t n, m; cin >> n >> m; - OY::DijkstraHeap::Graph G(n, m * 2); - // OY::DijkstraNaive::Graph G(n, m * 2); + OY::Dijkstra::Graph G(n, m * 2); for (uint32_t i = 0; i < m; i++) { uint32_t a, b, c; cin >> a >> b >> c; G.add_edge(a - 1, b - 1, c); G.add_edge(b - 1, a - 1, c); } - using Solver = OY::DijkstraHeap::Solver, double, false>; - // using Solver = OY::DijkstraNaive::Solver, double, false>; + using Solver = OY::Dijkstra::Solver, double, false>; std::vector sols; sols.reserve(n); - for (uint32_t i = 0; i != n; i++) sols.push_back(G.calc, double>(i)); - // for (uint32_t i = 0; i != n; i++) sols.push_back(G.calc, double>(i)); + for (uint32_t i = 0; i != n; i++) sols.push_back(G.calc, double>(i)); std::vector ans(n); for (uint32_t from = 0; from != n; from++) { @@ -129,8 +126,46 @@ void solve_spfa() { for (uint32_t v = 0; v != n; v++) write_double(ans[v]), cout << endl; } +void solve_dijk_naive() { + uint32_t n, m; + cin >> n >> m; + OY::StaticAddDijkstraNaive, 0x7fffffff, 0x7fffffff, 100> G(n); + for (uint32_t i = 0; i < m; i++) { + uint32_t a, b, c; + cin >> a >> b >> c; + G.add_edge(a - 1, b - 1, c); + G.add_edge(b - 1, a - 1, c); + } + std::vector> sols; + sols.reserve(n); + for (uint32_t i = 0; i != n; i++) sols.push_back(G.calc(i)); + + std::vector ans(n); + for (uint32_t from = 0; from != n; from++) { + for (uint32_t to = 0; to != n; to++) { + double tot = sols[from].query_count(to); + if (tot) + for (uint32_t v = 0; v != n; v++) { + if (v != from && v != to && sols[from].query(v) + sols[v].query(to) == sols[from].query(to)) { + ans[v] += sols[from].query_count(v) * sols[v].query_count(to) / tot; + } + } + } + } + + auto write_double = [](double x) { + x += 0.0005; + uint64_t x0 = x, x1 = (x - x0) * 1000; + cout << x0 << '.'; + auto str = std::to_string(x1); + cout << std::string(3 - str.size(), '0') + str; + }; + for (uint32_t v = 0; v != n; v++) write_double(ans[v]), cout << endl; +} + int main() { solve_floyd(); // solve_dijk(); // solve_spfa(); + // solve_dijk_naive(); } diff --git a/TEST/oj/luogu_p2865.cpp b/TEST/oj/luogu_p2865.cpp new file mode 100644 index 0000000..335832d --- /dev/null +++ b/TEST/oj/luogu_p2865.cpp @@ -0,0 +1,45 @@ +#include "GRAPH/DijkstraKPath.h" +#include "GRAPH/SPFAKPath.h" +#include "IO/FastIO.h" + +/* +[P2865 [USACO06NOV] Roadblocks G](https://www.luogu.com.cn/problem/P2865) + */ +/** + * 严格次短路模板题 + */ + +void solve_dijk() { + uint32_t n, m; + cin >> n >> m; + OY::DijkstraKPath::Graph G(n, m * 2); + for (uint32_t i = 0; i != m; i++) { + uint32_t a, b, w; + cin >> a >> b >> w; + G.add_edge(a - 1, b - 1, w); + G.add_edge(b - 1, a - 1, w); + } + + auto sol = G.calc<2>(0, n - 1); + cout << sol.query(n - 1, 1); +} + +void solve_spfa() { + uint32_t n, m; + cin >> n >> m; + OY::SPFAKPath::Graph G(n, m * 2); + for (uint32_t i = 0; i != m; i++) { + uint32_t a, b, w; + cin >> a >> b >> w; + G.add_edge(a - 1, b - 1, w); + G.add_edge(b - 1, a - 1, w); + } + + auto [sol, flag] = G.calc<2>(0); + cout << sol.query(n - 1, 1); +} + +int main() { + solve_dijk(); + // solve_spfa(); +} \ No newline at end of file diff --git a/TEST/oj/luogu_p3371.cpp b/TEST/oj/luogu_p3371.cpp index e3d98d7..9503605 100644 --- a/TEST/oj/luogu_p3371.cpp +++ b/TEST/oj/luogu_p3371.cpp @@ -1,5 +1,5 @@ #include "GRAPH/BellmanFord.h" -#include "GRAPH/Dijkstra_heap.h" +#include "GRAPH/Dijkstra.h" #include "GRAPH/Dijkstra_naive.h" #include "GRAPH/SPFA.h" #include "IO/FastIO.h" @@ -29,8 +29,50 @@ void solve_bf() { void solve_dijk() { uint32_t n, m, s; cin >> n >> m >> s; - OY::DijkstraHeap::Graph G(n, m); - // OY::DijkstraNaive::Graph G(n, m); + OY::Dijkstra::Graph G(n, m); + for (uint32_t i = 0; i < m; i++) { + uint32_t a, b, dis; + cin >> a >> b >> dis; + G.add_edge(a - 1, b - 1, dis); + } + + auto sol = G.calc(s - 1); + for (uint32_t i = 0; i < n; i++) cout << (sol.query_count(i) ? sol.query(i) : 2147483647) << ' '; +} + +// 本题空间紧张,所以重写了一个容器,暴力维护邻接表 +// 可以看到这个模板的自由度很高的,图存储形式可以自定义 +template +struct MapContainer { + template + using result_type = Node[10000]; + static constexpr bool is_vector = false; + std::vector> m_adj[10000]; + uint32_t m_vertex_cnt; + Tp m_infinite; + void resize(uint32_t vertex_cnt, const Tp &infinite) { m_vertex_cnt = vertex_cnt, m_infinite = infinite; } + Tp &get(uint32_t x, uint32_t y) { + auto it = std::find_if(m_adj[x].begin(), m_adj[x].end(), [y](auto &&e) { return e.first == y; }); + if (it != m_adj[x].end()) return it->second; + m_adj[x].emplace_back(y, m_infinite); + return m_adj[x].back().second; + } + const Tp &get(uint32_t x, uint32_t y) const { + auto it = std::find_if(m_adj[x].begin(), m_adj[x].end(), [y](auto &&e) { return e.first == y; }); + if (it != m_adj[x].end()) return it->second; + return m_infinite; + } + template + void enumerate(uint32_t from, Callback &&call) const { + for (auto &[to, dis] : m_adj[from]) call(to, dis); + } +}; + +void solve_dijk_naive() { + uint32_t n, m, s; + cin >> n >> m >> s; + using monoid = OY::DijkstraNaive::AddGroup; + OY::DijkstraNaive::Graph G(n); for (uint32_t i = 0; i < m; i++) { uint32_t a, b, dis; cin >> a >> b >> dis; @@ -44,4 +86,5 @@ void solve_dijk() { int main() { solve_bf(); // solve_dijk(); + // solve_dijk_naive(); } \ No newline at end of file diff --git a/TEST/oj/luogu_p4779.cpp b/TEST/oj/luogu_p4779.cpp index af8aafd..e25332e 100644 --- a/TEST/oj/luogu_p4779.cpp +++ b/TEST/oj/luogu_p4779.cpp @@ -1,4 +1,4 @@ -#include "GRAPH/Dijkstra_heap.h" +#include "GRAPH/Dijkstra.h" #include "IO/FastIO.h" /* @@ -11,7 +11,7 @@ int main() { uint32_t n, m, s; cin >> n >> m >> s; - OY::DijkstraHeap::Graph G(n, m); + OY::Dijkstra::Graph G(n, m); for (uint32_t i = 0; i < m; i++) { uint32_t a, b, dis; cin >> a >> b >> dis; diff --git a/TEST/oj/luogu_p6822.cpp b/TEST/oj/luogu_p6822.cpp index 560ed49..7c273b4 100644 --- a/TEST/oj/luogu_p6822.cpp +++ b/TEST/oj/luogu_p6822.cpp @@ -1,5 +1,5 @@ #include "DS/LinkBucket.h" -#include "GRAPH/Dijkstra_heap.h" +#include "GRAPH/Dijkstra.h" #include "IO/FastIO.h" /* @@ -52,7 +52,7 @@ void solve_dijk() { // 将原图的所有边划分成两个点 // 一个靠近 from,一个靠近 to,且距离为 dis - OY::DijkstraHeap::Graph G(m2 * 2, m2 * 4); + OY::Dijkstra::Graph G(m2 * 2, m2 * 4); // 内部连边 for (uint32_t i = 0; i != m2; i++) { G.add_edge(i * 2, i * 2 + 1, es[i].dis); @@ -72,7 +72,7 @@ void solve_dijk() { } } - using monoid = OY::DijkstraHeap::AddGroup; + using monoid = OY::Dijkstra::AddGroup; auto sol = G.calc(0, 3); cout << sol.query(3) << '\n'; } diff --git a/TEST/oj/uoj_622.cpp b/TEST/oj/uoj_622.cpp index 697be2a..9781016 100644 --- a/TEST/oj/uoj_622.cpp +++ b/TEST/oj/uoj_622.cpp @@ -1,4 +1,4 @@ -#include "GRAPH/Dijkstra_heap.h" +#include "GRAPH/Dijkstra.h" #include "IO/FastIO.h" /* @@ -11,14 +11,14 @@ int main() { uint32_t n, m, s; cin >> n >> m >> s; - OY::DijkstraHeap::Graph G(n, m); + OY::Dijkstra::Graph G(n, m); for (uint32_t i = 0; i < m; i++) { uint32_t a, b, dis; cin >> a >> b >> dis; G.add_edge(a - 1, b - 1, dis); } - using monoid = OY::DijkstraHeap::AddGroup; + using monoid = OY::Dijkstra::AddGroup; auto sol = G.calc(s - 1); for (uint32_t i = 0; i < n; i++) { auto x = sol.query(i); diff --git a/TEST/oj/ysp_173.cpp b/TEST/oj/ysp_173.cpp index cbe71db..34ff989 100644 --- a/TEST/oj/ysp_173.cpp +++ b/TEST/oj/ysp_173.cpp @@ -1,4 +1,4 @@ -#include "GRAPH/Dijkstra_heap.h" +#include "GRAPH/Dijkstra.h" #include "IO/FastIO.h" /* @@ -18,7 +18,7 @@ struct dist { int main() { uint32_t n, m, s, t; cin >> n >> m >> s >> t; - OY::DijkstraHeap::Graph G(n, m); + OY::Dijkstra::Graph G(n, m); for (uint32_t i = 0; i != m; i++) { uint32_t a, b, c; cin >> a >> b >> c; diff --git a/TREE/DoubleLCA.h b/TREE/DoubleLCA.h index ca3f150..5300a19 100644 --- a/TREE/DoubleLCA.h +++ b/TREE/DoubleLCA.h @@ -33,7 +33,7 @@ namespace OY { auto pre_work = [&](size_type a, size_type p) { fa[a] = p, m_dep[a] = ~p ? m_dep[p] + 1 : 0; }; m_rooted_tree->tree_dp_vertex(m_rooted_tree->m_root, pre_work, {}, {}); m_level = std::bit_width(*std::max_element(m_dep.begin(), m_dep.end())); - for (size_type j = 1; j != m_level; j++) { + for (size_type j = 1; j < m_level; j++) { m_fa[j].resize(m_rooted_tree->vertex_cnt()); size_type *prev = m_fa[j - 1].data(), *cur = m_fa[j].data(); for (size_type i = 0; i != m_rooted_tree->vertex_cnt(); i++) cur[i] = ~prev[i] ? prev[prev[i]] : -1; diff --git a/TREE/LongShortDecomposition.h b/TREE/LongShortDecomposition.h index b6a4147..381d6a7 100644 --- a/TREE/LongShortDecomposition.h +++ b/TREE/LongShortDecomposition.h @@ -61,7 +61,7 @@ namespace OY { m_fa[0].resize(m_rooted_tree->vertex_cnt()); _tree_dfs1(m_rooted_tree->m_root, -1); m_level = std::bit_width(m_info[m_rooted_tree->m_root].m_height - 1); - for (size_type j = 1; j != m_level; j++) { + for (size_type j = 1; j < m_level; j++) { m_fa[j].resize(m_rooted_tree->vertex_cnt()); size_type *prev = m_fa[j - 1].data(), *cur = m_fa[j].data(); for (size_type i = 0; i != m_rooted_tree->vertex_cnt(); i++) cur[i] = ~prev[i] ? prev[prev[i]] : -1; @@ -70,7 +70,7 @@ namespace OY { _tree_dfs2(m_rooted_tree->m_root, -1, cursor, m_info[m_rooted_tree->m_root].m_height); for (size_type i = 0; i != m_rooted_tree->vertex_cnt(); i++) if (~m_fa[0][i]) m_fa[0][i] = m_info[m_fa[0][i]].m_dfn + 1; - for (size_type j = 1; j != m_level; j++) { + for (size_type j = 1; j < m_level; j++) { size_type *cur = m_fa[j].data(); for (size_type i = 0; i != m_rooted_tree->vertex_cnt(); i++) if (~cur[i]) cur[i] = m_info[cur[i]].m_dfn + (1 << j);