-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
cfcf88c
commit 1999092
Showing
1 changed file
with
276 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,276 @@ | ||
// Created by pentagon03 on 2024/07/25 | ||
// Edited by pentagon03 on 2024/07/31 | ||
|
||
/* | ||
Reference: https://judge.yosupo.jp/submission/87297 | ||
We want: | ||
1. K_th shortest path 'values' from Min_Value Structure | ||
2. Given k, we want the actual 'k'th path | ||
*/ | ||
|
||
#include <vector> | ||
#include <queue> | ||
#include <deque> | ||
#include <string> | ||
#include <utility> | ||
#include <tuple> | ||
#include <cinttypes> | ||
#include <algorithm> | ||
#include <cassert> | ||
|
||
|
||
// Min Heap | ||
template <typename Key, typename Value> | ||
struct LeftistHeap { | ||
using self_t = LeftistHeap<Key, Value>; | ||
int rank; | ||
Key key; | ||
Value value; | ||
self_t *left, *right; | ||
LeftistHeap(int rank_, Key key_, Value value_, self_t* left_, | ||
self_t* right_) | ||
: rank{rank_}, key{key_}, value{value_}, left{left_}, right{right_} {} | ||
friend self_t* heap_insert(LeftistHeap* a, const Key k, const Value v, std::deque<LeftistHeap>& alloc) { | ||
if (not a or k < a->key) { | ||
alloc.emplace_back(1, k, v, a, nullptr); | ||
return &alloc.back(); | ||
} | ||
auto l = a->left, r = heap_insert(a->right, k, v, alloc); | ||
// adjust to l->rank >= r->rank | ||
if (not l or r->rank > l->rank) std::swap(l, r); | ||
alloc.emplace_back(r ? r->rank + 1 : 0, a->key, a->value, l, r); | ||
return &alloc.back(); | ||
} | ||
}; | ||
|
||
template <typename Distance, typename Graph> | ||
struct K_Shortest_Paths_Solver{ | ||
private: | ||
const Distance MAX_DISTANCE; | ||
Graph g; | ||
int n; | ||
bool is_dag; | ||
/* | ||
Information for Recovering path | ||
First we need previous values for each std::pair<Distance, heap_t*> | ||
1. heap_t values | ||
2. After we get the sequence of sidetracks, we need the best vertices | ||
*/ | ||
template <typename T> using min_heap = std::priority_queue<T, std::vector<T>, std::greater<T>>; | ||
using heap_t = LeftistHeap<Distance, std::pair<int, int>>; | ||
std::vector<Distance> d; | ||
std::vector<int> best; | ||
// for edge (u, v, w), sidetrack weight will be key, next vertex v will be value | ||
std::deque<heap_t> alloc; | ||
std::vector<heap_t*> h; | ||
std::vector<Distance> distances; | ||
std::vector<int> path_nodes; | ||
std::vector<std::pair<Distance, heap_t*>> nodes; | ||
std::vector<int> prev_node; | ||
public: | ||
explicit K_Shortest_Paths_Solver(Graph g_, bool is_dag_, Distance MAX_DISTANCE_) | ||
: MAX_DISTANCE(MAX_DISTANCE_), g(g_), n(g_.size()), is_dag(is_dag_){} | ||
// O(m log n) | ||
auto dijkstra(const Graph& g_, int s) { | ||
std::vector<Distance> d_(g_.size(), MAX_DISTANCE); | ||
std::vector<int> prv(g_.size(), -1); | ||
min_heap<std::pair<Distance, int>> heap; | ||
heap.emplace(d_[s] = Distance{}, s); | ||
while (!heap.empty()) { | ||
auto [dv, v] = heap.top(); | ||
heap.pop(); | ||
if (dv != d_[v]) continue; | ||
for (auto &[to, w] : g_[v]) { | ||
if (d_[to] > dv + w) { | ||
d_[to] = dv + w; | ||
heap.emplace(d_[to], to); | ||
prv[to] = v; | ||
} | ||
} | ||
} | ||
return std::make_pair(d_, prv); | ||
} | ||
auto topology_sort(const Graph& g_){ | ||
const int n_ = g_.size(); | ||
std::vector<int> in_deg(n_, 0); | ||
for(int u = 0; u < n_; u++) | ||
for(auto &[v, w]: g_[u]) | ||
in_deg[v]++; | ||
std::vector<int> order(n_); | ||
std::queue<int> q; | ||
for(int u = 0; u < n_; u++) | ||
if(!in_deg[u]) | ||
q.push(u); | ||
for(auto &u : order){ | ||
if(q.empty()) { | ||
assert(0 && "Cycle occured in DAG"); | ||
return order; | ||
} | ||
u = q.front(); | ||
q.pop(); | ||
for(auto &[v, w]: g_[u]) | ||
if(--in_deg[v] == 0) | ||
q.push(v); | ||
} | ||
return order; | ||
} | ||
// O(n + m) | ||
auto shortest_path_dag(const Graph& g_, int s){ | ||
std::vector<Distance> d_(g_.size(), MAX_DISTANCE); | ||
std::vector<int> prv(g_.size(), -1); | ||
d_[s] = Distance{}; | ||
auto order = topology_sort(g_); | ||
for(auto v: order){ | ||
if(d_[v] == MAX_DISTANCE) continue; | ||
for(auto &[to, w] : g_[v]){ | ||
if(d_[to] > d_[v] + w){ | ||
d_[to] = d_[v] + w; | ||
prv[to] = v; | ||
} | ||
} | ||
} | ||
return std::make_pair(d_, prv); | ||
} | ||
auto k_shortest_paths(int source, int sink, int k) { | ||
Graph g_rev(n); | ||
for (int u = 0; u < n; ++u) | ||
for (auto &[v, w] : g[u]) | ||
g_rev[v].push_back({u, w}); | ||
std::pair<std::vector<Distance>, std::vector<int>> info; | ||
if(is_dag) info = shortest_path_dag(g_rev, sink); | ||
else info = dijkstra(g_rev, sink); | ||
std::tie(d, best) = info; | ||
if (d[source] == MAX_DISTANCE) | ||
return std::vector<Distance>{}; | ||
std::vector<std::basic_string<int>> tree(n); | ||
for (int u = 0; u < n; ++u) | ||
if (best[u] != -1) | ||
tree[best[u]].push_back(u); // u will adopt best[u]'s heap | ||
alloc = std::deque<heap_t>{}; | ||
h = std::vector<heap_t*>(n, nullptr); | ||
{ | ||
std::queue<int> q({sink}); | ||
while (!q.empty()) { | ||
auto u = q.front(); | ||
q.pop(); | ||
bool seen_p = false; | ||
for (auto [v, w] : g[u]) { | ||
if (d[v] == MAX_DISTANCE) continue; | ||
auto c = w + d[v] - d[u]; | ||
if (not seen_p and v == best[u] and c == Distance{}) { | ||
seen_p = true; // we can only skip once! | ||
continue; | ||
} | ||
h[u] = heap_insert(h[u], c, {u, v}, alloc); | ||
} | ||
for (auto p : tree[u]) h[p] = h[u], q.push(p); // This works fast since this is basically a pointer | ||
} | ||
} | ||
// min distance | ||
distances = std::vector<Distance>{d[source]}; | ||
distances.reserve(k); | ||
// last sidetrack index of min_path. | ||
path_nodes = std::vector<int>{-1}; | ||
path_nodes.reserve(k); | ||
|
||
// {Distance, sidetrack ptr} | ||
nodes = std::vector<std::pair<Distance, heap_t*>>{}; | ||
nodes.reserve(3 * k); | ||
// previous node for each node | ||
prev_node = std::vector<int>{}; | ||
prev_node.reserve(3 * k); | ||
if (not h[source]) return distances; | ||
{ | ||
min_heap<std::tuple<Distance, heap_t*, int>> q; | ||
auto emplace = [&](const Distance& d,heap_t* h, int pre = -1){ | ||
int cur = (int) nodes.size(); | ||
q.emplace(d, h, cur); | ||
nodes.emplace_back(d, h); | ||
prev_node.push_back(pre); | ||
}; | ||
emplace(d[source] + h[source]->key, h[source]); | ||
while (!q.empty() && (int) distances.size() < k) { | ||
auto [cd, ch, cur] = q.top(); | ||
q.pop(); | ||
distances.push_back(cd); | ||
path_nodes.push_back(cur); | ||
if (h[ch->value.second]) emplace(cd + h[ch->value.second]->key, h[ch->value.second], cur); // add value | ||
if (ch->left) emplace(cd + ch->left->key - ch->key, ch->left, prev_node[cur]); // same heap, add difference | ||
if (ch->right) emplace(cd + ch->right->key - ch->key, ch->right, prev_node[cur]); // same heap, add difference | ||
} | ||
} | ||
return distances; | ||
} | ||
auto kth_shortest_full_path(int source, int sink, int k, bool call_k_paths = false) { | ||
if (call_k_paths) k_shortest_paths(source, sink, k + 1); | ||
std::vector<std::tuple<int, int, Distance>> path{}; | ||
if (k < 0 or k >= path_nodes.size()) | ||
return path; | ||
std::vector<std::tuple<int, int, Distance>> sidetracks{}; | ||
{ | ||
int cur = path_nodes[k]; | ||
while (cur != -1) { | ||
auto h = nodes[cur]; | ||
auto [u, v] = h.second->value; | ||
// sidetrack = d[v] + w - d[u] | ||
// w = sidetrack + d[u] - d[v]; | ||
auto w = h.second->key + d[u] - d[v]; | ||
sidetracks.emplace_back(u, v, w); | ||
cur = prev_node[cur]; | ||
} | ||
std::reverse(sidetracks.begin(), sidetracks.end()); | ||
} | ||
{ | ||
int idx = 0; | ||
int cur = source; | ||
using std::get; | ||
while (cur != sink or idx < (int) sidetracks.size()){ | ||
if(idx < (int) sidetracks.size() && cur == get<0>(sidetracks[idx])){ | ||
path.push_back(sidetracks[idx]); | ||
cur = get<1>(sidetracks[idx]); | ||
idx++; | ||
}else{ | ||
int nxt = best[cur]; | ||
path.emplace_back(cur, nxt, d[cur] - d[nxt]); | ||
cur = nxt; | ||
} | ||
} | ||
} | ||
return path; | ||
} | ||
|
||
}; | ||
|
||
using Graph = std::vector<std::vector<std::pair<int,int>>>; | ||
|
||
#include<limits> | ||
#include<iostream> | ||
int32_t main(){ | ||
std::cin.tie(0)->sync_with_stdio(0); | ||
int _tests = 1; | ||
// cin >> _tests; | ||
for (int _test = 1; _test <= _tests; ++_test) { | ||
// cout << "Case #" << _test << ": "; | ||
int n, m, s, t, k; | ||
std::cin >> n >> m >> s >> t >> k; | ||
Graph g(n); | ||
for (int i = 0; i < m; ++i) { | ||
int u, v, w; | ||
std::cin >> u >> v >> w; | ||
g[u].push_back({v, w}); | ||
} | ||
K_Shortest_Paths_Solver<int64_t, Graph> sol(g, false, std::numeric_limits<int64_t>::max()); | ||
auto ans = sol.k_shortest_paths(s, t, k); | ||
ans.resize(k, -1); | ||
for (int idx = -1; auto x : ans){ | ||
idx += 1; | ||
std::cout << x << '\n'; | ||
// auto path = sol.kth_shortest_full_path(s, t, idx, false); | ||
// std::cout << "path" << '\n'; | ||
// for(auto &[from, to, w]: path) | ||
// std::cout << from << ' ' << to << ' ' << w << '\n'; | ||
// std::cout << '\n'; | ||
} | ||
} | ||
} |