This documentation is automatically generated by online-judge-tools/verification-helper
#include "../shortest_path.hpp"
#include "../../data_structure/radix_heap.hpp"
#include <algorithm>
#include <iostream>
#include <vector>
#define PROBLEM "https://judge.yosupo.jp/problem/shortest_path"
using namespace std;
int main() {
int N, M, s, t;
cin >> N >> M >> s >> t;
constexpr long long INF = 1LL << 60;
shortest_path<long long, INF> graph(N);
while (M--) {
int a, b, c;
cin >> a >> b >> c;
graph.add_edge(a, b, c);
}
graph.dijkstra<radix_heap<unsigned long long, int>>(s, t);
auto d_radix = graph.dist;
graph.solve(s);
assert(graph.dist[t] == d_radix[t]);
if (graph.dist[t] == INF) {
cout << "-1\n";
return 0;
}
vector<int> path;
int now = t;
while (true) {
path.push_back(now);
if (now == s) break;
now = graph.prev[now];
}
std::reverse(path.begin(), path.end());
cout << graph.dist[t] << ' ' << path.size() - 1 << '\n';
for (unsigned i = 0; i + 1 < path.size(); i++) cout << path[i] << ' ' << path[i + 1] << '\n';
}
#line 2 "graph/shortest_path.hpp"
#include <algorithm>
#include <cassert>
#include <deque>
#include <fstream>
#include <functional>
#include <limits>
#include <queue>
#include <string>
#include <tuple>
#include <utility>
#include <vector>
template <typename T, T INF = std::numeric_limits<T>::max() / 2, int INVALID = -1>
struct shortest_path {
int V, E;
bool single_positive_weight;
T wmin, wmax;
std::vector<std::pair<int, T>> tos;
std::vector<int> head;
std::vector<std::tuple<int, int, T>> edges;
void build_() {
if (int(tos.size()) == E and int(head.size()) == V + 1) return;
tos.resize(E);
head.assign(V + 1, 0);
for (const auto &e : edges) ++head[std::get<0>(e) + 1];
for (int i = 0; i < V; ++i) head[i + 1] += head[i];
auto cur = head;
for (const auto &e : edges) {
tos[cur[std::get<0>(e)]++] = std::make_pair(std::get<1>(e), std::get<2>(e));
}
}
shortest_path(int V = 0) : V(V), E(0), single_positive_weight(true), wmin(0), wmax(0) {}
void add_edge(int s, int t, T w) {
assert(0 <= s and s < V);
assert(0 <= t and t < V);
edges.emplace_back(s, t, w);
++E;
if (w > 0 and wmax > 0 and wmax != w) single_positive_weight = false;
wmin = std::min(wmin, w);
wmax = std::max(wmax, w);
}
void add_bi_edge(int u, int v, T w) {
add_edge(u, v, w);
add_edge(v, u, w);
}
std::vector<T> dist;
std::vector<int> prev;
// Dijkstra algorithm
// - Requirement: wmin >= 0
// - Complexity: O(E log E)
using Pque = std::priority_queue<std::pair<T, int>, std::vector<std::pair<T, int>>,
std::greater<std::pair<T, int>>>;
template <class Heap = Pque> void dijkstra(int s, int t = INVALID) {
assert(0 <= s and s < V);
build_();
dist.assign(V, INF);
prev.assign(V, INVALID);
dist[s] = 0;
Heap pq;
pq.emplace(0, s);
while (!pq.empty()) {
T d;
int v;
std::tie(d, v) = pq.top();
pq.pop();
if (t == v) return;
if (dist[v] < d) continue;
for (int e = head[v]; e < head[v + 1]; ++e) {
const auto &nx = tos[e];
T dnx = d + nx.second;
if (dist[nx.first] > dnx) {
dist[nx.first] = dnx, prev[nx.first] = v;
pq.emplace(dnx, nx.first);
}
}
}
}
// Dijkstra algorithm
// - Requirement: wmin >= 0
// - Complexity: O(V^2 + E)
void dijkstra_vquad(int s, int t = INVALID) {
assert(0 <= s and s < V);
build_();
dist.assign(V, INF);
prev.assign(V, INVALID);
dist[s] = 0;
std::vector<char> fixed(V, false);
while (true) {
int r = INVALID;
T dr = INF;
for (int i = 0; i < V; i++) {
if (!fixed[i] and dist[i] < dr) r = i, dr = dist[i];
}
if (r == INVALID or r == t) break;
fixed[r] = true;
int nxt;
T dx;
for (int e = head[r]; e < head[r + 1]; ++e) {
std::tie(nxt, dx) = tos[e];
if (dist[nxt] > dist[r] + dx) dist[nxt] = dist[r] + dx, prev[nxt] = r;
}
}
}
// Bellman-Ford algorithm
// - Requirement: no negative loop
// - Complexity: O(VE)
bool bellman_ford(int s, int nb_loop) {
assert(0 <= s and s < V);
build_();
dist.assign(V, INF), prev.assign(V, INVALID);
dist[s] = 0;
for (int l = 0; l < nb_loop; l++) {
bool upd = false;
for (int v = 0; v < V; v++) {
if (dist[v] == INF) continue;
for (int e = head[v]; e < head[v + 1]; ++e) {
const auto &nx = tos[e];
T dnx = dist[v] + nx.second;
if (dist[nx.first] > dnx) dist[nx.first] = dnx, prev[nx.first] = v, upd = true;
}
}
if (!upd) return true;
}
return false;
}
// Bellman-ford algorithm using deque
// - Requirement: no negative loop
// - Complexity: O(VE)
void spfa(int s) {
assert(0 <= s and s < V);
build_();
dist.assign(V, INF);
prev.assign(V, INVALID);
dist[s] = 0;
std::deque<int> q;
std::vector<char> in_queue(V);
q.push_back(s), in_queue[s] = 1;
while (!q.empty()) {
int now = q.front();
q.pop_front(), in_queue[now] = 0;
for (int e = head[now]; e < head[now + 1]; ++e) {
const auto &nx = tos[e];
T dnx = dist[now] + nx.second;
int nxt = nx.first;
if (dist[nxt] > dnx) {
dist[nxt] = dnx;
if (!in_queue[nxt]) {
if (q.size() and dnx < dist[q.front()]) { // Small label first optimization
q.push_front(nxt);
} else {
q.push_back(nxt);
}
prev[nxt] = now, in_queue[nxt] = 1;
}
}
}
}
}
// 01-BFS
// - Requirement: all weights must be 0 or w (positive constant).
// - Complexity: O(V + E)
void zero_one_bfs(int s, int t = INVALID) {
assert(0 <= s and s < V);
build_();
dist.assign(V, INF), prev.assign(V, INVALID);
dist[s] = 0;
std::vector<int> q(V * 4);
int ql = V * 2, qr = V * 2;
q[qr++] = s;
while (ql < qr) {
int v = q[ql++];
if (v == t) return;
for (int e = head[v]; e < head[v + 1]; ++e) {
const auto &nx = tos[e];
T dnx = dist[v] + nx.second;
if (dist[nx.first] > dnx) {
dist[nx.first] = dnx, prev[nx.first] = v;
if (nx.second) {
q[qr++] = nx.first;
} else {
q[--ql] = nx.first;
}
}
}
}
}
// Dial's algorithm
// - Requirement: wmin >= 0
// - Complexity: O(wmax * V + E)
void dial(int s, int t = INVALID) {
assert(0 <= s and s < V);
build_();
dist.assign(V, INF), prev.assign(V, INVALID);
dist[s] = 0;
std::vector<std::vector<std::pair<int, T>>> q(wmax + 1);
q[0].emplace_back(s, dist[s]);
int ninq = 1;
int cur = 0;
T dcur = 0;
for (; ninq; ++cur, ++dcur) {
if (cur == wmax + 1) cur = 0;
while (!q[cur].empty()) {
int v = q[cur].back().first;
T dnow = q[cur].back().second;
q[cur].pop_back(), --ninq;
if (v == t) return;
if (dist[v] < dnow) continue;
for (int e = head[v]; e < head[v + 1]; ++e) {
const auto &nx = tos[e];
T dnx = dist[v] + nx.second;
if (dist[nx.first] > dnx) {
dist[nx.first] = dnx, prev[nx.first] = v;
int nxtcur = cur + int(nx.second);
if (nxtcur >= int(q.size())) nxtcur -= q.size();
q[nxtcur].emplace_back(nx.first, dnx), ++ninq;
}
}
}
}
}
// Solver for DAG
// - Requirement: graph is DAG
// - Complexity: O(V + E)
bool dag_solver(int s) {
assert(0 <= s and s < V);
build_();
dist.assign(V, INF), prev.assign(V, INVALID);
dist[s] = 0;
std::vector<int> indeg(V, 0);
std::vector<int> q(V * 2);
int ql = 0, qr = 0;
q[qr++] = s;
while (ql < qr) {
int now = q[ql++];
for (int e = head[now]; e < head[now + 1]; ++e) {
const auto &nx = tos[e];
++indeg[nx.first];
if (indeg[nx.first] == 1) q[qr++] = nx.first;
}
}
ql = qr = 0;
q[qr++] = s;
while (ql < qr) {
int now = q[ql++];
for (int e = head[now]; e < head[now + 1]; ++e) {
const auto &nx = tos[e];
--indeg[nx.first];
if (dist[nx.first] > dist[now] + nx.second)
dist[nx.first] = dist[now] + nx.second, prev[nx.first] = now;
if (indeg[nx.first] == 0) q[qr++] = nx.first;
}
}
return *max_element(indeg.begin(), indeg.end()) == 0;
}
// Retrieve a sequence of vertex ids that represents shortest path [s, ..., goal]
// If not reachable to goal, return {}
std::vector<int> retrieve_path(int goal) const {
assert(int(prev.size()) == V);
assert(0 <= goal and goal < V);
if (dist[goal] == INF) return {};
std::vector<int> ret{goal};
while (prev[goal] != INVALID) {
goal = prev[goal];
ret.push_back(goal);
}
std::reverse(ret.begin(), ret.end());
return ret;
}
void solve(int s, int t = INVALID) {
if (wmin >= 0) {
if (single_positive_weight) {
zero_one_bfs(s, t);
} else if (wmax <= 10) {
dial(s, t);
} else {
if ((long long)V * V < (E << 4)) {
dijkstra_vquad(s, t);
} else {
dijkstra(s, t);
}
}
} else {
bellman_ford(s, V);
}
}
// Warshall-Floyd algorithm
// - Requirement: no negative loop
// - Complexity: O(E + V^3)
std::vector<std::vector<T>> floyd_warshall() {
build_();
std::vector<std::vector<T>> dist2d(V, std::vector<T>(V, INF));
for (int i = 0; i < V; i++) {
dist2d[i][i] = 0;
for (const auto &e : edges) {
int s = std::get<0>(e), t = std::get<1>(e);
dist2d[s][t] = std::min(dist2d[s][t], std::get<2>(e));
}
}
for (int k = 0; k < V; k++) {
for (int i = 0; i < V; i++) {
if (dist2d[i][k] == INF) continue;
for (int j = 0; j < V; j++) {
if (dist2d[k][j] == INF) continue;
dist2d[i][j] = std::min(dist2d[i][j], dist2d[i][k] + dist2d[k][j]);
}
}
}
return dist2d;
}
void to_dot(std::string filename = "shortest_path") const {
std::ofstream ss(filename + ".DOT");
ss << "digraph{\n";
build_();
for (int i = 0; i < V; i++) {
for (int e = head[i]; e < head[i + 1]; ++e) {
ss << i << "->" << tos[e].first << "[label=" << tos[e].second << "];\n";
}
}
ss << "}\n";
ss.close();
return;
}
};
#line 2 "data_structure/radix_heap.hpp"
#include <array>
#include <cstddef>
#line 6 "data_structure/radix_heap.hpp"
#include <type_traits>
#line 9 "data_structure/radix_heap.hpp"
// Radix heap for unsigned integer
// https://github.com/iwiwi/radix-heap
template <class Uint, class Label, typename std::enable_if<std::is_unsigned<Uint>::value>::type * = nullptr>
class radix_heap {
int sz;
Uint last;
std::array<std::vector<std::pair<Uint, Label>>, std::numeric_limits<Uint>::digits + 1> v;
template <class U, typename std::enable_if<sizeof(U) == 4>::type * = nullptr>
static inline int bucket(U x) noexcept {
return x ? 32 - __builtin_clz(x) : 0;
}
template <class U, typename std::enable_if<sizeof(U) == 8>::type * = nullptr>
static inline int bucket(U x) noexcept {
return x ? 64 - __builtin_clzll(x) : 0;
}
void pull() {
if (!v[0].empty()) return;
int i = 1;
while (v[i].empty()) ++i;
last = v[i].back().first;
for (int j = 0; j < int(v[i].size()); j++) last = std::min(last, v[i][j].first);
for (int j = 0; j < int(v[i].size()); j++) {
v[bucket(v[i][j].first ^ last)].emplace_back(std::move(v[i][j]));
}
v[i].clear();
}
public:
radix_heap() : sz(0), last(0) {
static_assert(std::numeric_limits<Uint>::digits > 0, "Invalid type.");
}
std::size_t size() const noexcept { return sz; }
bool empty() const noexcept { return sz == 0; }
void push(Uint x, const Label &val) { ++sz, v[bucket(x ^ last)].emplace_back(x, val); }
void push(Uint x, Label &&val) { ++sz, v[bucket(x ^ last)].emplace_back(x, std::move(val)); }
template <class... Args> void emplace(Uint x, Args &&...args) {
++sz, v[bucket(x ^ last)].emplace_back(std::piecewise_construct, std::forward_as_tuple(x),
std::forward_as_tuple(args...));
}
void pop() { pull(), --sz, v[0].pop_back(); }
std::pair<Uint, Label> top() { return pull(), v[0].back(); }
Uint top_key() { return pull(), last; }
Label &top_label() { return pull(), v[0].back().second; }
void clear() noexcept {
sz = 0, last = 0;
for (auto &vec : v) vec.clear();
}
void swap(radix_heap<Uint, Label> &a) {
std::swap(sz, a.sz), std::swap(last, a.last), v.swap(a.v);
}
};
#line 4 "graph/test/shortest_path.test.cpp"
#include <iostream>
#line 6 "graph/test/shortest_path.test.cpp"
#define PROBLEM "https://judge.yosupo.jp/problem/shortest_path"
using namespace std;
int main() {
int N, M, s, t;
cin >> N >> M >> s >> t;
constexpr long long INF = 1LL << 60;
shortest_path<long long, INF> graph(N);
while (M--) {
int a, b, c;
cin >> a >> b >> c;
graph.add_edge(a, b, c);
}
graph.dijkstra<radix_heap<unsigned long long, int>>(s, t);
auto d_radix = graph.dist;
graph.solve(s);
assert(graph.dist[t] == d_radix[t]);
if (graph.dist[t] == INF) {
cout << "-1\n";
return 0;
}
vector<int> path;
int now = t;
while (true) {
path.push_back(now);
if (now == s) break;
now = graph.prev[now];
}
std::reverse(path.begin(), path.end());
cout << graph.dist[t] << ' ' << path.size() - 1 << '\n';
for (unsigned i = 0; i + 1 < path.size(); i++) cout << path[i] << ' ' << path[i + 1] << '\n';
}