Last active
August 31, 2025 14:01
-
-
Save yuygfgg/0f8941c21cd66c1e3bead040c0f396a2 to your computer and use it in GitHub Desktop.
Shortest non-separating st-path on chordal graphs
This file contains hidden or 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
| #include <algorithm> | |
| #include <array> | |
| #include <cstdint> | |
| #include <cstdio> | |
| #include <cstring> | |
| #include <iostream> | |
| #include <iterator> | |
| #include <limits> | |
| #include <numeric> | |
| #include <queue> | |
| #include <unordered_map> | |
| #include <unordered_set> | |
| #include <utility> | |
| #include <vector> | |
| inline size_t next_power_of_2(size_t n) { | |
| if (n <= 1) | |
| return 1; | |
| return 1ull << (64 - __builtin_clzll(n - 1)); | |
| } | |
| using namespace std; | |
| constexpr int64_t INF = numeric_limits<int64_t>::max() / 2; | |
| constexpr int32_t INF_INT = numeric_limits<int32_t>::max(); | |
| typedef int32_t Vertex; | |
| typedef vector<Vertex> Path; | |
| using BoolVector = vector<bool>; | |
| constexpr Vertex V_NONE = 0; | |
| typedef uint64_t PackedKey; | |
| struct optimized_hash { | |
| static uint64_t splitmix64(uint64_t x) { | |
| x += 0x9e3779b97f4a7c15; | |
| x = (x ^ (x >> 30)) * 0xbf58476d1ce4e5b9; | |
| x = (x ^ (x >> 27)) * 0x94d049bb133111eb; | |
| return x ^ (x >> 31); | |
| } | |
| template <typename T> size_t operator()(T x) const { | |
| static const uint64_t FIXED_RANDOM = | |
| reinterpret_cast<uint64_t>(&FIXED_RANDOM); | |
| return splitmix64(static_cast<uint64_t>(x) + FIXED_RANDOM); | |
| } | |
| }; | |
| inline PackedKey edge_key(Vertex u, Vertex v) { | |
| if (u > v) | |
| swap(u, v); | |
| return ((uint64_t)v << 32) | (uint32_t)u; | |
| } | |
| inline PackedKey directed_edge_key(Vertex u, Vertex v) { | |
| return ((uint64_t)u << 32) | (uint32_t)v; | |
| } | |
| inline PackedKey lr_key(int32_t r_id, Vertex u) { | |
| return ((uint64_t)r_id << 32) | (uint32_t)u; | |
| } | |
| inline pair<Vertex, Vertex> unpack_edge_key(PackedKey key) { | |
| return {(Vertex)(key & 0xFFFFFFFF), (Vertex)(key >> 32)}; | |
| } | |
| template <typename V> | |
| using OptimizedMap = unordered_map<PackedKey, V, optimized_hash>; | |
| template <typename K> using OptimizedSet = unordered_set<K, optimized_hash>; | |
| struct FlatMap { | |
| struct Slot { | |
| PackedKey k; | |
| Vertex v; | |
| }; | |
| vector<Slot> tab; | |
| vector<uint8_t> used; | |
| size_t mask; | |
| FlatMap() : mask(0) {} | |
| void init(size_t capacity_estimate) { | |
| if (capacity_estimate == 0) { | |
| mask = 0; | |
| return; | |
| } | |
| size_t cap_pow2 = | |
| next_power_of_2(capacity_estimate + capacity_estimate / 2); | |
| if (cap_pow2 < 16) | |
| cap_pow2 = 16; | |
| if (cap_pow2 == 0) { | |
| mask = 0; | |
| return; | |
| } | |
| tab.assign(cap_pow2, {}); | |
| used.assign(cap_pow2, 0); | |
| mask = cap_pow2 - 1; | |
| } | |
| inline void emplace(PackedKey k, Vertex v) { | |
| if (mask == 0) | |
| return; | |
| size_t i = optimized_hash::splitmix64(k) & mask; | |
| while (used[i] && tab[i].k != k) | |
| i = (i + 1) & mask; | |
| if (!used[i]) { | |
| used[i] = 1; | |
| tab[i].k = k; | |
| tab[i].v = v; | |
| } | |
| } | |
| inline Vertex get(PackedKey k) const { | |
| if (mask == 0) | |
| return V_NONE; | |
| size_t i = optimized_hash::splitmix64(k) & mask; | |
| while (used[i]) { | |
| if (tab[i].k == k) | |
| return tab[i].v; | |
| i = (i + 1) & mask; | |
| } | |
| return V_NONE; | |
| } | |
| }; | |
| struct Edge { | |
| int64_t weight; | |
| Vertex to; | |
| int32_t id; | |
| }; | |
| struct OriginalEdge { | |
| Vertex u, v; | |
| int64_t weight; | |
| int32_t id; | |
| OriginalEdge(Vertex u, Vertex v, int64_t w, int32_t id) | |
| : u(u), v(v), weight(w), id(id) {} | |
| }; | |
| class Graph { | |
| public: | |
| vector<vector<Edge>> adj; | |
| int32_t N; | |
| bool finalized = false; | |
| vector<OriginalEdge> edge_storage; | |
| vector<const OriginalEdge*> edge_list; | |
| vector<const OriginalEdge*> edge_id_to_ptr; | |
| int32_t max_edge_id = -1; | |
| Graph() : N(0) {} | |
| Graph(int32_t vertex_count, int32_t edge_count) { | |
| N = vertex_count + 1; | |
| adj.resize(N); | |
| if (edge_count > 0) { | |
| edge_storage.reserve(edge_count); | |
| edge_list.reserve(edge_count); | |
| edge_id_to_ptr.reserve(edge_count); | |
| } | |
| } | |
| void finalize_construction() { | |
| if (finalized) | |
| return; | |
| for (Vertex u = 1; u < N; ++u) { | |
| sort(adj[u].begin(), adj[u].end(), | |
| [](const Edge& a, const Edge& b) { return a.to < b.to; }); | |
| } | |
| finalized = true; | |
| } | |
| void ensure_vertex_capacity(Vertex u) { | |
| if (u >= N) { | |
| N = u + 1; | |
| adj.resize(N); | |
| finalized = false; | |
| } | |
| } | |
| void ensure_id_capacity(int32_t id) { | |
| if (id != -1 && id >= static_cast<int32_t>(edge_id_to_ptr.size())) { | |
| edge_id_to_ptr.resize(id + 1, nullptr); | |
| } | |
| } | |
| void add_directed_edge(Vertex u, Vertex v, int64_t w, int32_t id) { | |
| adj[u].push_back({w, v, id}); | |
| } | |
| void add_edge(Vertex u, Vertex v, int64_t w, int32_t id, | |
| const OriginalEdge* existing_ptr = nullptr) { | |
| if (id != -1) { | |
| if (id > max_edge_id) { | |
| max_edge_id = id; | |
| ensure_id_capacity(id); | |
| } else if (id >= static_cast<int32_t>(edge_id_to_ptr.size())) { | |
| ensure_id_capacity(id); | |
| } | |
| if (id < static_cast<int32_t>(edge_id_to_ptr.size()) && | |
| edge_id_to_ptr[id]) { | |
| return; | |
| } | |
| } | |
| if (max(u, v) >= N) { | |
| ensure_vertex_capacity(max(u, v)); | |
| } | |
| add_directed_edge(u, v, w, id); | |
| add_directed_edge(v, u, w, id); | |
| finalized = false; | |
| if (id != -1) { | |
| const OriginalEdge* ptr; | |
| if (existing_ptr) { | |
| ptr = existing_ptr; | |
| } else { | |
| edge_storage.emplace_back(u, v, w, id); | |
| ptr = &edge_storage.back(); | |
| } | |
| edge_list.push_back(ptr); | |
| if (id < static_cast<int32_t>(edge_id_to_ptr.size())) { | |
| edge_id_to_ptr[id] = ptr; | |
| } | |
| } | |
| } | |
| inline pair<int64_t, int32_t> get_edge_info(Vertex u, Vertex v) const { | |
| if (u >= N || v >= N || u == 0 || v == 0) | |
| return {INF, -1}; | |
| const vector<Edge>* adj_list; | |
| Vertex target; | |
| if (adj[u].size() <= adj[v].size()) { | |
| adj_list = &adj[u]; | |
| target = v; | |
| } else { | |
| adj_list = &adj[v]; | |
| target = u; | |
| } | |
| if (finalized) { | |
| auto it = std::lower_bound( | |
| adj_list->begin(), adj_list->end(), target, | |
| [](const Edge& e, Vertex t) { return e.to < t; }); | |
| if (it != adj_list->end() && it->to == target) { | |
| return {it->weight, it->id}; | |
| } | |
| } else { | |
| for (const auto& edge : *adj_list) { | |
| if (edge.to == target) { | |
| return {edge.weight, edge.id}; | |
| } | |
| } | |
| } | |
| return {INF, -1}; | |
| } | |
| bool are_adjacent(Vertex u, Vertex v) const { | |
| return get_edge_info(u, v).second != -1; | |
| } | |
| const OriginalEdge* get_edge_ptr(int32_t id) const { | |
| if (id < 0 || id >= static_cast<int32_t>(edge_id_to_ptr.size())) | |
| return nullptr; | |
| return edge_id_to_ptr[id]; | |
| } | |
| }; | |
| class MultiGraph { | |
| public: | |
| vector<vector<pair<Vertex, int32_t>>> adj; | |
| int32_t N; | |
| MultiGraph(int32_t size) : N(size) { adj.resize(N); } | |
| }; | |
| inline int64_t get_edge_length(const Graph& G, Vertex u, Vertex v) { | |
| return G.get_edge_info(u, v).first; | |
| } | |
| inline int32_t get_edge_id(const Graph& G, Vertex u, Vertex v) { | |
| return G.get_edge_info(u, v).second; | |
| } | |
| inline vector<int32_t> get_P_indices_vec(const Path& P, int32_t N) { | |
| vector<int32_t> indices(N, -1); | |
| for (int32_t i = 0; i < static_cast<int32_t>(P.size()); ++i) { | |
| if (P[i] > 0 && P[i] < N) { | |
| indices[P[i]] = i; | |
| } | |
| } | |
| return indices; | |
| } | |
| inline bool is_useful_weighted(const Path& r, const vector<int64_t>& weights, | |
| const Graph& G) { | |
| if (r.size() < 3) | |
| return false; | |
| for (int32_t i = 0; i <= static_cast<int32_t>(r.size()) - 3; ++i) { | |
| int64_t len_path = weights[i] + weights[i + 1]; | |
| int64_t len_edge = get_edge_length(G, r[i], r[i + 2]); | |
| if (len_edge != INF && len_path >= len_edge) { | |
| return false; | |
| } | |
| } | |
| return true; | |
| } | |
| inline pair<int64_t, Path> dijkstra(const Graph& G, Vertex S, Vertex T, | |
| const BoolVector& is_forbidden_v = {}, | |
| const BoolVector& is_forbidden_e = {}) { | |
| if (S <= 0 || S >= G.N || T <= 0 || T >= G.N) | |
| return {INF, {}}; | |
| if (!is_forbidden_v.empty()) { | |
| if ((S < static_cast<int32_t>(is_forbidden_v.size()) && | |
| is_forbidden_v[S]) || | |
| (T < static_cast<int32_t>(is_forbidden_v.size()) && | |
| is_forbidden_v[T])) | |
| return {INF, {}}; | |
| } | |
| vector<int64_t> dist(G.N, INF); | |
| vector<Vertex> parent(G.N, V_NONE); | |
| using PQElement = pair<int64_t, Vertex>; | |
| priority_queue<PQElement, vector<PQElement>, greater<PQElement>> pq; | |
| dist[S] = 0; | |
| pq.emplace(0, S); | |
| while (!pq.empty()) { | |
| int64_t d = pq.top().first; | |
| Vertex u = pq.top().second; | |
| pq.pop(); | |
| if (d > dist[u]) | |
| continue; | |
| if (u == T) | |
| break; | |
| for (const auto& edge : G.adj[u]) { | |
| Vertex v = edge.to; | |
| if (!is_forbidden_v.empty() && | |
| v < static_cast<int32_t>(is_forbidden_v.size()) && | |
| is_forbidden_v[v]) | |
| continue; | |
| if (edge.id != -1 && !is_forbidden_e.empty() && | |
| edge.id < static_cast<int32_t>(is_forbidden_e.size()) && | |
| is_forbidden_e[edge.id]) | |
| continue; | |
| int64_t new_dist = dist[u] + edge.weight; | |
| if (new_dist < dist[v]) { | |
| dist[v] = new_dist; | |
| parent[v] = u; | |
| pq.emplace(dist[v], v); | |
| } | |
| } | |
| } | |
| if (dist[T] == INF) { | |
| return {INF, {}}; | |
| } | |
| Path path; | |
| Vertex curr = T; | |
| while (curr != S) { | |
| path.push_back(curr); | |
| if (parent[curr] == V_NONE) | |
| return {INF, {}}; | |
| curr = parent[curr]; | |
| } | |
| path.push_back(S); | |
| reverse(path.begin(), path.end()); | |
| return {dist[T], path}; | |
| } | |
| class BridgeFinder { | |
| public: | |
| const Graph& G; | |
| vector<int32_t> tin, low; | |
| int timer; | |
| BoolVector is_bridge; | |
| int max_id = -1; | |
| BridgeFinder(const Graph& G) : G(G) {} | |
| struct DFSState { | |
| Vertex v; | |
| Vertex p; | |
| int edge_id_in; | |
| size_t neighbor_index; | |
| }; | |
| inline void find_Bridges() { | |
| timer = 0; | |
| tin.assign(G.N, -1); | |
| low.assign(G.N, -1); | |
| vector<int32_t> bridge_ids_vec; | |
| bridge_ids_vec.reserve(G.edge_list.size()); | |
| vector<DFSState> stack; | |
| if (G.N > 1) | |
| stack.reserve(G.N); | |
| for (Vertex start_node = 1; start_node < G.N; ++start_node) { | |
| if (tin[start_node] != -1) | |
| continue; | |
| stack.push_back({start_node, V_NONE, -1, 0}); | |
| while (!stack.empty()) { | |
| DFSState& state = stack.back(); | |
| if (state.neighbor_index == 0) { | |
| if (tin[state.v] == -1) { | |
| tin[state.v] = low[state.v] = timer++; | |
| } | |
| } | |
| const auto& adj_v = G.adj[state.v]; | |
| bool pushed_child = false; | |
| while (state.neighbor_index < adj_v.size()) { | |
| const auto& edge = adj_v[state.neighbor_index]; | |
| Vertex to = edge.to; | |
| state.neighbor_index++; | |
| if (to == state.p) | |
| continue; | |
| if (tin[to] != -1) { | |
| low[state.v] = min(low[state.v], tin[to]); | |
| } else { | |
| stack.push_back({to, state.v, edge.id, 0}); | |
| pushed_child = true; | |
| break; | |
| } | |
| } | |
| if (pushed_child) { | |
| continue; | |
| } | |
| Vertex v = state.v; | |
| Vertex p = state.p; | |
| int32_t edge_id_in = state.edge_id_in; | |
| stack.pop_back(); | |
| if (p != V_NONE) { | |
| low[p] = min(low[p], low[v]); | |
| if (low[v] > tin[p]) { | |
| if (edge_id_in != -1) { | |
| bridge_ids_vec.push_back(edge_id_in); | |
| if (edge_id_in > max_id) { | |
| max_id = edge_id_in; | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| if (max_id != -1) { | |
| is_bridge.assign(max_id + 1, false); | |
| for (int32_t id : bridge_ids_vec) { | |
| is_bridge[id] = true; | |
| } | |
| } | |
| } | |
| }; | |
| class BCCFinder { | |
| public: | |
| const Graph& G; | |
| const BoolVector* forbidden_edge_ids = nullptr; | |
| vector<int32_t> tin, low; | |
| int32_t timer; | |
| vector<vector<Vertex>> BCCs; | |
| vector<int> mark_v_internal; | |
| int stamp_counter_internal; | |
| vector<vector<int32_t>> vertex_BCC_membership; | |
| BoolVector is_ap; | |
| vector<int32_t> edge_stack; | |
| vector<int32_t> edge_to_BCC; | |
| BCCFinder(const Graph& G, const BoolVector* forbidden = nullptr) | |
| : G(G), forbidden_edge_ids(forbidden) {} | |
| struct DFSState { | |
| Vertex v; | |
| Vertex p; | |
| int32_t edge_id_in; | |
| size_t neighbor_index; | |
| int32_t children_count; | |
| }; | |
| inline void find_BCCs() { | |
| timer = 0; | |
| tin.assign(G.N, -1); | |
| low.assign(G.N, -1); | |
| BCCs.clear(); | |
| stamp_counter_internal = 1; | |
| mark_v_internal.assign(G.N, 0); | |
| vertex_BCC_membership.assign(G.N, {}); | |
| is_ap.assign(G.N, false); | |
| edge_stack.clear(); | |
| edge_stack.reserve(G.edge_list.size()); | |
| if (G.max_edge_id != -1) { | |
| edge_to_BCC.assign(G.max_edge_id + 1, -1); | |
| } else { | |
| edge_to_BCC.clear(); | |
| } | |
| vector<DFSState> stack; | |
| if (G.N > 1) | |
| stack.reserve(G.N); | |
| for (Vertex start_node = 1; start_node < G.N; ++start_node) { | |
| if (tin[start_node] != -1) | |
| continue; | |
| stack.push_back({start_node, V_NONE, -1, 0, 0}); | |
| while (!stack.empty()) { | |
| DFSState& state = stack.back(); | |
| if (state.neighbor_index == 0) { | |
| if (tin[state.v] == -1) { | |
| tin[state.v] = low[state.v] = timer++; | |
| } | |
| } | |
| const auto& adj_v = G.adj[state.v]; | |
| bool pushed_child = false; | |
| while (state.neighbor_index < adj_v.size()) { | |
| const auto& edge = adj_v[state.neighbor_index]; | |
| Vertex to = edge.to; | |
| state.neighbor_index++; | |
| if (to == state.p) | |
| continue; | |
| if (forbidden_edge_ids && edge.id != -1 && | |
| edge.id < | |
| static_cast<int32_t>(forbidden_edge_ids->size()) && | |
| (*forbidden_edge_ids)[edge.id]) { | |
| continue; | |
| } | |
| if (tin[to] != -1) { | |
| low[state.v] = min(low[state.v], tin[to]); | |
| if (tin[to] < tin[state.v]) { | |
| if (edge.id != -1) { | |
| edge_stack.push_back(edge.id); | |
| } | |
| } | |
| } else { | |
| if (edge.id != -1) { | |
| edge_stack.push_back(edge.id); | |
| } | |
| stack.push_back({to, state.v, edge.id, 0, 0}); | |
| pushed_child = true; | |
| break; | |
| } | |
| } | |
| if (pushed_child) { | |
| continue; | |
| } | |
| Vertex v = state.v; | |
| Vertex p = state.p; | |
| int32_t edge_id_in = state.edge_id_in; | |
| int32_t children_count = state.children_count; | |
| stack.pop_back(); | |
| if (p != V_NONE) { | |
| DFSState& parent_state = stack.back(); | |
| parent_state.children_count++; | |
| low[p] = min(low[p], low[v]); | |
| if (low[v] >= tin[p]) { | |
| if (parent_state.p != V_NONE) { | |
| is_ap[p] = true; | |
| } | |
| int32_t bcc_index = BCCs.size(); | |
| vector<Vertex> component_vertices_with_dups; | |
| int32_t critical_edge_id = edge_id_in; | |
| while (!edge_stack.empty()) { | |
| int32_t e_id = edge_stack.back(); | |
| bool is_critical = (e_id == critical_edge_id); | |
| edge_stack.pop_back(); | |
| const OriginalEdge* e_ptr = G.get_edge_ptr(e_id); | |
| if (e_ptr) { | |
| component_vertices_with_dups.push_back( | |
| e_ptr->u); | |
| component_vertices_with_dups.push_back( | |
| e_ptr->v); | |
| } | |
| if (e_id != -1 && e_id < static_cast<int32_t>( | |
| edge_to_BCC.size())) { | |
| edge_to_BCC[e_id] = bcc_index; | |
| } | |
| if (is_critical) | |
| break; | |
| } | |
| if (!component_vertices_with_dups.empty()) { | |
| vector<Vertex> component; | |
| component.reserve( | |
| component_vertices_with_dups.size()); | |
| stamp_counter_internal++; | |
| for (Vertex u : component_vertices_with_dups) { | |
| if (u < G.N && mark_v_internal[u] != | |
| stamp_counter_internal) { | |
| mark_v_internal[u] = stamp_counter_internal; | |
| component.push_back(u); | |
| if (u < static_cast<Vertex>( | |
| vertex_BCC_membership.size())) { | |
| vertex_BCC_membership[u].push_back( | |
| bcc_index); | |
| } | |
| } | |
| } | |
| if (!component.empty()) { | |
| BCCs.push_back(std::move(component)); | |
| } | |
| } | |
| } | |
| } else { | |
| if (children_count > 1) { | |
| is_ap[v] = true; | |
| } | |
| } | |
| } | |
| } | |
| } | |
| inline bool is_in_BCC(int bcc_idx, Vertex v) const { | |
| if (v <= 0 || v >= G.N) | |
| return false; | |
| if (bcc_idx < 0 || bcc_idx >= static_cast<int32_t>(BCCs.size())) | |
| return false; | |
| const auto& memberships = vertex_BCC_membership[v]; | |
| for (int32_t id : memberships) { | |
| if (id == bcc_idx) | |
| return true; | |
| if (id > bcc_idx) | |
| return false; | |
| } | |
| return false; | |
| } | |
| }; | |
| class BCCFinder_Multi { | |
| public: | |
| const MultiGraph& MG; | |
| vector<int32_t> tin, low; | |
| int32_t timer; | |
| vector<vector<int32_t>> BCC_edges; | |
| vector<int32_t> edge_stack; | |
| BCCFinder_Multi(const MultiGraph& MG) : MG(MG) {} | |
| struct DFSState { | |
| Vertex v; | |
| Vertex p; | |
| int32_t parent_edge_id; | |
| size_t neighbor_index; | |
| }; | |
| inline void find_BCCs() { | |
| timer = 0; | |
| tin.assign(MG.N, -1); | |
| low.assign(MG.N, -1); | |
| BCC_edges.clear(); | |
| edge_stack.clear(); | |
| vector<DFSState> stack; | |
| if (MG.N > 1) | |
| stack.reserve(MG.N); | |
| for (Vertex start_node = 1; start_node < MG.N; ++start_node) { | |
| if (tin[start_node] != -1) | |
| continue; | |
| if (MG.adj[start_node].empty()) | |
| continue; | |
| stack.push_back({start_node, V_NONE, -1, 0}); | |
| while (!stack.empty()) { | |
| DFSState& state = stack.back(); | |
| if (state.neighbor_index == 0) { | |
| if (tin[state.v] == -1) { | |
| tin[state.v] = low[state.v] = timer++; | |
| } | |
| } | |
| const auto& adj_v = MG.adj[state.v]; | |
| bool pushed_child = false; | |
| while (state.neighbor_index < adj_v.size()) { | |
| const auto& edge_info = adj_v[state.neighbor_index]; | |
| Vertex to = edge_info.first; | |
| int32_t current_edge_id = edge_info.second; | |
| state.neighbor_index++; | |
| if (current_edge_id == state.parent_edge_id) | |
| continue; | |
| if (tin[to] != -1) { | |
| low[state.v] = min(low[state.v], tin[to]); | |
| if (tin[to] < tin[state.v]) { | |
| edge_stack.push_back(current_edge_id); | |
| } | |
| } else { | |
| edge_stack.push_back(current_edge_id); | |
| stack.push_back({to, state.v, current_edge_id, 0}); | |
| pushed_child = true; | |
| break; | |
| } | |
| } | |
| if (pushed_child) | |
| continue; | |
| Vertex v = state.v; | |
| Vertex p = state.p; | |
| int32_t parent_edge_id = state.parent_edge_id; | |
| stack.pop_back(); | |
| if (p != V_NONE) { | |
| low[p] = min(low[p], low[v]); | |
| if (low[v] >= tin[p]) { | |
| vector<int32_t> component; | |
| while (!edge_stack.empty()) { | |
| int32_t e_id = edge_stack.back(); | |
| edge_stack.pop_back(); | |
| component.push_back(e_id); | |
| if (e_id == parent_edge_id) | |
| break; | |
| } | |
| if (!component.empty()) { | |
| BCC_edges.push_back(std::move(component)); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| }; | |
| struct BCTree { | |
| int32_t N; | |
| vector<vector<int32_t>> adj; | |
| int32_t get_ap_id(Vertex v) const { return v; } | |
| int32_t get_bcc_id(int32_t i) const { return N + i; } | |
| inline void build(const BCCFinder& bcc_finder, int32_t graph_N) { | |
| N = graph_N; | |
| const auto& BCCs = bcc_finder.BCCs; | |
| const auto& is_ap = bcc_finder.is_ap; | |
| int32_t total_nodes = N + BCCs.size(); | |
| adj.clear(); | |
| adj.resize(total_nodes); | |
| for (int32_t i = 0; i < static_cast<int32_t>(BCCs.size()); ++i) { | |
| int32_t bcc_id = get_bcc_id(i); | |
| for (Vertex v : BCCs[i]) { | |
| if (v < static_cast<int32_t>(is_ap.size()) && is_ap[v]) { | |
| int32_t ap_id = get_ap_id(v); | |
| adj[bcc_id].push_back(ap_id); | |
| adj[ap_id].push_back(bcc_id); | |
| } | |
| } | |
| } | |
| } | |
| inline vector<int32_t> find_path(int32_t start_node, int32_t end_node) { | |
| if (start_node == -1 || end_node == -1) | |
| return {}; | |
| if (start_node == end_node) | |
| return {start_node}; | |
| int32_t total_nodes = adj.size(); | |
| if (start_node >= total_nodes || end_node >= total_nodes || | |
| start_node <= 0 || end_node <= 0) | |
| return {}; | |
| queue<int32_t> q; | |
| vector<int32_t> parent(total_nodes, -1); | |
| q.push(start_node); | |
| parent[start_node] = 0; | |
| bool found = false; | |
| while (!q.empty()) { | |
| int32_t u = q.front(); | |
| q.pop(); | |
| if (u == end_node) { | |
| found = true; | |
| break; | |
| } | |
| for (const int v : adj[u]) { | |
| if (parent[v] == -1) { | |
| parent[v] = u; | |
| q.push(v); | |
| } | |
| } | |
| } | |
| if (!found) | |
| return {}; | |
| vector<int32_t> path; | |
| int32_t curr = end_node; | |
| while (curr != 0) { | |
| path.push_back(curr); | |
| curr = parent[curr]; | |
| } | |
| reverse(path.begin(), path.end()); | |
| return path; | |
| } | |
| }; | |
| inline BoolVector compute_BAD_G(const Graph& G, Vertex S, Vertex T) { | |
| BCCFinder bcc_finder(G); | |
| bcc_finder.find_BCCs(); | |
| BCTree bct; | |
| bct.build(bcc_finder, G.N); | |
| const auto& is_ap = bcc_finder.is_ap; | |
| const auto& BCCs = bcc_finder.BCCs; | |
| vector<int32_t> vertex_to_bcc(G.N, -1); | |
| for (int32_t i = 0; i < static_cast<int32_t>(BCCs.size()); ++i) { | |
| for (Vertex v : BCCs[i]) { | |
| if (v < G.N && | |
| (v >= static_cast<int32_t>(is_ap.size()) || !is_ap[v])) { | |
| vertex_to_bcc[v] = i; | |
| } | |
| } | |
| } | |
| auto find_tau = [&](Vertex V) { | |
| if (V <= 0 || V >= G.N) | |
| return -1; | |
| if (V < static_cast<int32_t>(is_ap.size()) && is_ap[V]) { | |
| return bct.get_ap_id(V); | |
| } else { | |
| int32_t bcc_index = vertex_to_bcc[V]; | |
| if (bcc_index != -1) | |
| return bct.get_bcc_id(bcc_index); | |
| } | |
| return -1; | |
| }; | |
| int32_t S_tau = find_tau(S); | |
| int32_t T_tau = find_tau(T); | |
| vector<int32_t> path = bct.find_path(S_tau, T_tau); | |
| BoolVector is_bad(G.N, false); | |
| vector<int32_t> degrees_in_C_dat(G.N, 0); | |
| BoolVector in_C_ws(G.N, false); | |
| for (int32_t index = 0; index < static_cast<int32_t>(path.size()); | |
| ++index) { | |
| const int32_t node_id = path[index]; | |
| if (node_id >= G.N) { | |
| int32_t bcc_index = node_id - G.N; | |
| if (bcc_index < 0 || bcc_index >= static_cast<int32_t>(BCCs.size())) | |
| continue; | |
| const auto& C = BCCs[bcc_index]; | |
| for (Vertex v : C) { | |
| if (v < G.N) | |
| in_C_ws[v] = true; | |
| } | |
| for (Vertex u : C) { | |
| int32_t degree = 0; | |
| if (u < G.N) { | |
| for (const auto& edge : G.adj[u]) { | |
| if (edge.to < G.N && in_C_ws[edge.to]) | |
| degree++; | |
| } | |
| degrees_in_C_dat[u] = degree; | |
| } | |
| } | |
| for (Vertex u : C) { | |
| if (u == S || u == T || u >= G.N) | |
| continue; | |
| if (degrees_in_C_dat[u] == 2) { | |
| bool u_is_AP_connector = false; | |
| if (u < static_cast<int32_t>(is_ap.size()) && is_ap[u]) { | |
| int32_t u_ap_id = bct.get_ap_id(u); | |
| if (index > 0 && path[index - 1] == u_ap_id) { | |
| u_is_AP_connector = true; | |
| } | |
| if (!u_is_AP_connector && | |
| index < static_cast<int32_t>(path.size()) - 1 && | |
| path[index + 1] == u_ap_id) { | |
| u_is_AP_connector = true; | |
| } | |
| } | |
| if (!u_is_AP_connector) { | |
| is_bad[u] = true; | |
| } | |
| } | |
| } | |
| for (Vertex v : C) { | |
| if (v < G.N) | |
| in_C_ws[v] = false; | |
| } | |
| } | |
| } | |
| return is_bad; | |
| } | |
| struct DSU { | |
| vector<int> fa, sz; | |
| DSU(int n = 0) { init(n); } | |
| void init(int n) { | |
| fa.resize(n); | |
| sz.assign(n, 1); | |
| std::iota(fa.begin(), fa.end(), 0); | |
| } | |
| int find(int x) { | |
| while (fa[x] != x) { | |
| fa[x] = fa[fa[x]]; | |
| x = fa[x]; | |
| } | |
| return x; | |
| } | |
| void unite(int a, int b) { | |
| a = find(a); | |
| b = find(b); | |
| if (a == b) | |
| return; | |
| if (sz[a] < sz[b]) | |
| swap(a, b); | |
| fa[b] = a; | |
| sz[a] += sz[b]; | |
| } | |
| }; | |
| inline Path validate_separator(const vector<const OriginalEdge*>& R, | |
| const Graph& G, bool required_odd_P_index, | |
| const vector<int32_t>& P_indices, | |
| const vector<int32_t>& min_P_neighbor_index, | |
| vector<vector<pair<Vertex, int64_t>>>& adj_R_ws, | |
| BoolVector& in_R_ws) { | |
| if (R.empty()) | |
| return {}; | |
| const int32_t N = G.N; | |
| vector<Vertex> vertices_R; | |
| vertices_R.reserve(R.size() + 1); | |
| for (const auto* edge_ptr : R) { | |
| Vertex u = edge_ptr->u; | |
| Vertex v = edge_ptr->v; | |
| int64_t w = edge_ptr->weight; | |
| if (u >= N || v >= N || u == 0 || v == 0) | |
| continue; | |
| adj_R_ws[u].push_back({v, w}); | |
| adj_R_ws[v].push_back({u, w}); | |
| if (!in_R_ws[u]) { | |
| in_R_ws[u] = true; | |
| vertices_R.push_back(u); | |
| } | |
| if (!in_R_ws[v]) { | |
| in_R_ws[v] = true; | |
| vertices_R.push_back(v); | |
| } | |
| } | |
| auto cleanup = [&]() { | |
| for (Vertex v : vertices_R) { | |
| in_R_ws[v] = false; | |
| adj_R_ws[v].clear(); | |
| } | |
| }; | |
| vector<Vertex> endpoints; | |
| for (Vertex v : vertices_R) { | |
| const auto& neighbors = adj_R_ws[v]; | |
| if (neighbors.size() > 2) { | |
| cleanup(); | |
| return {}; | |
| } | |
| if (neighbors.size() == 1) | |
| endpoints.push_back(v); | |
| } | |
| if (endpoints.size() != 2) { | |
| cleanup(); | |
| return {}; | |
| } | |
| Path r; | |
| vector<int64_t> r_weights; | |
| r.reserve(R.size() + 1); | |
| r_weights.reserve(R.size()); | |
| Vertex curr = endpoints[0]; | |
| Vertex prev = V_NONE; | |
| while (curr != V_NONE) { | |
| r.push_back(curr); | |
| Vertex next = V_NONE; | |
| const auto& neighbors = adj_R_ws[curr]; | |
| for (const auto& edge_info : neighbors) { | |
| Vertex neighbor = edge_info.first; | |
| int64_t weight = edge_info.second; | |
| if (neighbor != prev) { | |
| next = neighbor; | |
| r_weights.push_back(weight); | |
| break; | |
| } | |
| } | |
| prev = curr; | |
| curr = next; | |
| } | |
| cleanup(); | |
| if (r.size() != R.size() + 1) | |
| return {}; | |
| auto check_alignment = [&](const Path& path) { | |
| int32_t last_P_idx = -1; | |
| for (Vertex v : path) { | |
| if (v < static_cast<int32_t>(P_indices.size()) && | |
| P_indices[v] != -1) { | |
| if (P_indices[v] <= last_P_idx) | |
| return false; | |
| last_P_idx = P_indices[v]; | |
| } | |
| } | |
| return true; | |
| }; | |
| if (!check_alignment(r)) { | |
| reverse(r.begin(), r.end()); | |
| reverse(r_weights.begin(), r_weights.end()); | |
| if (!check_alignment(r)) | |
| return {}; | |
| } | |
| if (r.size() < 4) | |
| return {}; | |
| for (size_t i = 2; i < r.size(); ++i) { | |
| if (r[i] >= static_cast<int32_t>(P_indices.size()) || | |
| P_indices[r[i]] == -1) | |
| return {}; | |
| } | |
| int32_t idx_r2 = P_indices[r[2]]; | |
| if ((idx_r2 % 2 != 0) != required_odd_P_index) | |
| return {}; | |
| if (!is_useful_weighted(r, r_weights, G)) | |
| return {}; | |
| Vertex r0 = r[0]; | |
| bool traversable = false; | |
| if (r0 < static_cast<int32_t>(P_indices.size()) && P_indices[r0] != -1) { | |
| if (P_indices[r0] < idx_r2) | |
| traversable = true; | |
| } | |
| if (!traversable) { | |
| if (r0 < static_cast<int32_t>(min_P_neighbor_index.size()) && | |
| min_P_neighbor_index[r0] != INF_INT) { | |
| if (min_P_neighbor_index[r0] < idx_r2) | |
| traversable = true; | |
| } | |
| } | |
| if (!traversable) | |
| return {}; | |
| return r; | |
| } | |
| inline void compute_S_Separators(const Graph& G, const Path& P, | |
| vector<Path>& X_result) { | |
| if (P.empty()) | |
| return; | |
| vector<int32_t> P_indices = get_P_indices_vec(P, G.N); | |
| vector<int32_t> min_P_neighbor_index(G.N, INF_INT); | |
| for (int32_t i = 0; i < static_cast<int32_t>(P.size()); ++i) { | |
| Vertex u = P[i]; | |
| if (u >= G.N || u == 0) | |
| continue; | |
| if (i < min_P_neighbor_index[u]) | |
| min_P_neighbor_index[u] = i; | |
| for (const auto& edge : G.adj[u]) { | |
| Vertex v = edge.to; | |
| if (v >= G.N || v == 0) | |
| continue; | |
| if (i < min_P_neighbor_index[v]) | |
| min_P_neighbor_index[v] = i; | |
| } | |
| } | |
| BoolVector adj2; | |
| if (P.size() >= 3) { | |
| size_t n = P.size(); | |
| adj2.resize(n - 2); | |
| for (size_t i = 0; i < n - 2; ++i) { | |
| adj2[i] = (get_edge_length(G, P[i], P[i + 2]) != INF); | |
| } | |
| } | |
| vector<vector<pair<Vertex, int64_t>>> adj_R_reusable(G.N); | |
| BoolVector in_R_reusable(G.N, false); | |
| int32_t max_id = G.max_edge_id; | |
| BoolVector in_C_edges_ws; | |
| if (max_id != -1) { | |
| in_C_edges_ws.resize(max_id + 1, false); | |
| } | |
| for (int parity_type = 0; parity_type < 2; ++parity_type) { | |
| bool find_odd_S_sep = (parity_type == 0); | |
| bool merge_even_P_indices = find_odd_S_sep; | |
| DSU dsu(G.N); | |
| if (P.size() >= 3) { | |
| for (int32_t i = 0; i <= static_cast<int32_t>(P.size()) - 3; ++i) { | |
| bool parity_match = | |
| merge_even_P_indices ? (i % 2 == 0) : (i % 2 != 0); | |
| if (parity_match && adj2[i]) { | |
| dsu.unite(P[i], P[i + 2]); | |
| } | |
| } | |
| } | |
| for (Vertex u = 1; u < G.N; ++u) { | |
| if (P_indices[u] != -1) | |
| continue; | |
| if (min_P_neighbor_index[u] == INF_INT) | |
| continue; | |
| int min_index = min_P_neighbor_index[u]; | |
| for (const auto& edge : G.adj[u]) { | |
| Vertex v = edge.to; | |
| if (v >= G.N || v == 0) | |
| continue; | |
| if (P_indices[v] != -1) { | |
| int idx_v = P_indices[v]; | |
| bool parity_match = merge_even_P_indices ? (idx_v % 2 == 0) | |
| : (idx_v % 2 != 0); | |
| if (parity_match && idx_v > min_index) { | |
| dsu.unite(u, v); | |
| } | |
| } | |
| } | |
| } | |
| vector<int> deg_fat(G.N, 0); | |
| for (const auto* edge_ptr : G.edge_list) { | |
| Vertex u = edge_ptr->u, v = edge_ptr->v; | |
| if (u >= G.N || v >= G.N || u == 0 || v == 0) | |
| continue; | |
| int fu = dsu.find(u), fv = dsu.find(v); | |
| if (fu != fv) { | |
| deg_fat[fu]++; | |
| deg_fat[fv]++; | |
| } | |
| } | |
| vector<int32_t> old_to_new_id(G.N, 0); | |
| int32_t new_N = 1; | |
| for (Vertex i = 1; i < G.N; ++i) { | |
| if (deg_fat[i] > 0) { | |
| old_to_new_id[i] = new_N++; | |
| } | |
| } | |
| MultiGraph GMERGE(new_N); | |
| for (Vertex i = 1; i < G.N; ++i) { | |
| if (deg_fat[i] > 0) { | |
| int32_t new_id = old_to_new_id[i]; | |
| if (new_id > 0 && new_id < new_N) { | |
| GMERGE.adj[new_id].reserve(deg_fat[i]); | |
| } | |
| } | |
| } | |
| for (const auto* edge_ptr : G.edge_list) { | |
| Vertex u = edge_ptr->u; | |
| Vertex v = edge_ptr->v; | |
| if (u >= G.N || v >= G.N || u == 0 || v == 0) | |
| continue; | |
| int fu = dsu.find(u); | |
| int fv = dsu.find(v); | |
| if (fu != fv) { | |
| int32_t new_fu = old_to_new_id[fu]; | |
| int32_t new_fv = old_to_new_id[fv]; | |
| if (new_fu > 0 && new_fv > 0) { | |
| int edge_id = edge_ptr->id; | |
| GMERGE.adj[new_fu].push_back({new_fv, edge_id}); | |
| GMERGE.adj[new_fv].push_back({new_fu, edge_id}); | |
| } | |
| } | |
| } | |
| BCCFinder_Multi bcc_finder(GMERGE); | |
| bcc_finder.find_BCCs(); | |
| vector<Vertex> C_compact_vertices_vec; | |
| BoolVector in_C_compact_vertices_ws(new_N, false); | |
| for (const auto& C_edges : bcc_finder.BCC_edges) { | |
| C_compact_vertices_vec.clear(); | |
| for (int32_t edge_id : C_edges) { | |
| if (edge_id >= 0 && edge_id <= max_id) | |
| in_C_edges_ws[edge_id] = true; | |
| } | |
| for (int32_t edge_id : C_edges) { | |
| const OriginalEdge* edge_ptr = G.get_edge_ptr(edge_id); | |
| if (!edge_ptr) | |
| continue; | |
| Vertex u = edge_ptr->u; | |
| Vertex v = edge_ptr->v; | |
| if (u >= G.N || v >= G.N || u == 0 || v == 0) | |
| continue; | |
| Vertex compact_u = old_to_new_id[dsu.find(u)]; | |
| Vertex compact_v = old_to_new_id[dsu.find(v)]; | |
| if (compact_u != V_NONE && | |
| !in_C_compact_vertices_ws[compact_u]) { | |
| in_C_compact_vertices_ws[compact_u] = true; | |
| C_compact_vertices_vec.push_back(compact_u); | |
| } | |
| if (compact_v != V_NONE && | |
| !in_C_compact_vertices_ws[compact_v]) { | |
| in_C_compact_vertices_ws[compact_v] = true; | |
| C_compact_vertices_vec.push_back(compact_v); | |
| } | |
| } | |
| for (Vertex u_new : C_compact_vertices_vec) { | |
| if (u_new > 0 && u_new < GMERGE.N) { | |
| vector<const OriginalEdge*> R; | |
| R.reserve(GMERGE.adj[u_new].size()); | |
| for (const auto& edge_info : GMERGE.adj[u_new]) { | |
| int edge_id = edge_info.second; | |
| if (edge_id >= 0 && edge_id <= max_id && | |
| in_C_edges_ws[edge_id]) { | |
| R.push_back(G.get_edge_ptr(edge_id)); | |
| } | |
| } | |
| Path r = validate_separator(R, G, find_odd_S_sep, P_indices, | |
| min_P_neighbor_index, | |
| adj_R_reusable, in_R_reusable); | |
| if (!r.empty()) { | |
| X_result.push_back(std::move(r)); | |
| } | |
| } | |
| } | |
| for (int32_t edge_id : C_edges) { | |
| if (edge_id >= 0 && edge_id <= max_id) | |
| in_C_edges_ws[edge_id] = false; | |
| } | |
| for (Vertex v : C_compact_vertices_vec) { | |
| in_C_compact_vertices_ws[v] = false; | |
| } | |
| } | |
| } | |
| } | |
| inline vector<Path> compute_X_ST(const Graph& G, const Path& P) { | |
| vector<Path> X_ST_candidates; | |
| compute_S_Separators(G, P, X_ST_candidates); | |
| size_t X_S_size = X_ST_candidates.size(); | |
| Path P_rev = P; | |
| reverse(P_rev.begin(), P_rev.end()); | |
| compute_S_Separators(G, P_rev, X_ST_candidates); | |
| for (size_t i = X_S_size; i < X_ST_candidates.size(); ++i) { | |
| reverse(X_ST_candidates[i].begin(), X_ST_candidates[i].end()); | |
| } | |
| return X_ST_candidates; | |
| } | |
| inline vector<Path> compute_X_EXTRA(const Graph& G, const Path& P0) { | |
| if (P0.empty()) | |
| return {}; | |
| vector<int64_t> P0_weights; | |
| if (!P0.empty()) { | |
| P0_weights.reserve(P0.size() - 1); | |
| } | |
| BoolVector is_edge_on_P0_vec; | |
| bool use_vec = (G.max_edge_id != -1); | |
| if (use_vec) { | |
| is_edge_on_P0_vec.resize(G.max_edge_id + 1, false); | |
| } | |
| for (size_t i = 0; i < P0.size() - 1; ++i) { | |
| Vertex u = P0[i], v = P0[i + 1]; | |
| pair<int64_t, int32_t> info = G.get_edge_info(u, v); | |
| P0_weights.push_back(info.first); | |
| int32_t id = info.second; | |
| if (use_vec) { | |
| if (id != -1 && | |
| id < static_cast<int32_t>(is_edge_on_P0_vec.size())) { | |
| is_edge_on_P0_vec[id] = true; | |
| } else if (id != -1) { | |
| use_vec = false; | |
| is_edge_on_P0_vec.clear(); | |
| is_edge_on_P0_vec.shrink_to_fit(); | |
| } | |
| } | |
| } | |
| OptimizedSet<PackedKey> edges_P0_set; | |
| if (!use_vec) { | |
| edges_P0_set.reserve(P0.size() - 1); | |
| for (size_t i = 0; i < P0.size() - 1; ++i) { | |
| edges_P0_set.insert(edge_key(P0[i], P0[i + 1])); | |
| } | |
| } | |
| vector<int32_t> component_id(G.N, 0); | |
| int comp_count = 0; | |
| queue<Vertex> q; | |
| for (Vertex v = 1; v < G.N; ++v) { | |
| if (component_id[v] == 0) { | |
| comp_count++; | |
| q.push(v); | |
| component_id[v] = comp_count; | |
| while (!q.empty()) { | |
| Vertex u = q.front(); | |
| q.pop(); | |
| for (const auto& edge : G.adj[u]) { | |
| Vertex neighbor = edge.to; | |
| bool on_P0 = false; | |
| if (use_vec) { | |
| if (edge.id != -1 && | |
| edge.id < static_cast<int32_t>( | |
| is_edge_on_P0_vec.size())) { | |
| on_P0 = is_edge_on_P0_vec[edge.id]; | |
| } | |
| } else { | |
| on_P0 = edges_P0_set.count(edge_key(u, neighbor)); | |
| } | |
| if (on_P0) | |
| continue; | |
| if (neighbor < G.N && component_id[neighbor] == 0) { | |
| component_id[neighbor] = comp_count; | |
| q.push(neighbor); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| vector<int32_t> BEL(P0.size()); | |
| for (int32_t i = 0; i < static_cast<int32_t>(P0.size()); ++i) { | |
| if (P0[i] < G.N) | |
| BEL[i] = component_id[P0[i]]; | |
| } | |
| vector<Path> X_EXTRA; | |
| int32_t i = 0; | |
| for (int32_t j = 1; j < static_cast<int32_t>(P0.size()); ++j) { | |
| if (j > 1 && BEL[j] != BEL[j - 2]) { | |
| if (i < j - 1) { | |
| Path candidate; | |
| vector<int64_t> candidate_weights; | |
| for (int32_t k = i; k <= j - 1; ++k) { | |
| candidate.push_back(P0[k]); | |
| if (k < j - 1) | |
| candidate_weights.push_back(P0_weights[k]); | |
| } | |
| if (candidate.size() >= 4 && | |
| is_useful_weighted(candidate, candidate_weights, G)) { | |
| X_EXTRA.push_back(std::move(candidate)); | |
| } | |
| } | |
| i = j - 1; | |
| } | |
| if (j > 0 && BEL[j] == BEL[j - 1]) | |
| i = j; | |
| } | |
| if (i < static_cast<int32_t>(P0.size()) - 1) { | |
| Path candidate; | |
| vector<int64_t> candidate_weights; | |
| for (int32_t k = i; k < static_cast<int32_t>(P0.size()); ++k) { | |
| candidate.push_back(P0[k]); | |
| if (k < static_cast<int32_t>(P0.size()) - 1) | |
| candidate_weights.push_back(P0_weights[k]); | |
| } | |
| if (candidate.size() >= 4 && | |
| is_useful_weighted(candidate, candidate_weights, G)) { | |
| X_EXTRA.push_back(std::move(candidate)); | |
| } | |
| } | |
| return X_EXTRA; | |
| } | |
| struct LR_Cache { | |
| mutable FlatMap L_cache; | |
| mutable FlatMap R_cache; | |
| }; | |
| struct AVOID_precomputation { | |
| const Graph& G; | |
| const vector<Path>& X; | |
| vector<int32_t> inner_r_id; | |
| vector<int32_t> inner_idx; | |
| vector<uint8_t> on_r_endpoint; | |
| BoolVector is_edge_on_X; | |
| vector<uint8_t> is_X_head_edge; | |
| vector<vector<int32_t>> X_edge_ids; | |
| BCCFinder bcc_finder; | |
| vector<int32_t> head_rid_fwd; | |
| vector<int32_t> head_rid_rev; | |
| vector<Vertex> head_r2_fwd; | |
| vector<Vertex> head_r2_rev; | |
| LR_Cache lr_cache; | |
| AVOID_precomputation(const Graph& graph, const vector<Path>& paths) | |
| : G(graph), X(paths), bcc_finder(G) { | |
| inner_r_id.assign(G.N, -1); | |
| inner_idx.assign(G.N, -1); | |
| on_r_endpoint.assign(G.N, 0); | |
| int max_id = G.max_edge_id; | |
| if (max_id != -1) { | |
| is_edge_on_X.assign(max_id + 1, false); | |
| is_X_head_edge.assign(max_id + 1, 0); | |
| head_rid_fwd.assign(max_id + 1, -1); | |
| head_rid_rev.assign(max_id + 1, -1); | |
| head_r2_fwd.assign(max_id + 1, V_NONE); | |
| head_r2_rev.assign(max_id + 1, V_NONE); | |
| } | |
| X_edge_ids.resize(X.size()); | |
| size_t total_edges_X = 0; | |
| for (int32_t r_id = 0; r_id < static_cast<int32_t>(X.size()); ++r_id) { | |
| const Path& r = X[r_id]; | |
| if (r.empty()) | |
| continue; | |
| if (r.size() > 1) { | |
| X_edge_ids[r_id].reserve(r.size() - 1); | |
| total_edges_X += r.size() - 1; | |
| } | |
| if (r.front() < G.N) | |
| on_r_endpoint[r.front()] |= 1; | |
| if (r.back() < G.N) | |
| on_r_endpoint[r.back()] |= 2; | |
| for (size_t i = 1; i < r.size() - 1; ++i) { | |
| if (r[i] < G.N) { | |
| inner_r_id[r[i]] = r_id; | |
| inner_idx[r[i]] = i; | |
| } | |
| } | |
| for (size_t i = 0; i < r.size() - 1; ++i) { | |
| int32_t edge_id = get_edge_id(G, r[i], r[i + 1]); | |
| X_edge_ids[r_id].push_back(edge_id); | |
| if (edge_id != -1 && | |
| edge_id < static_cast<int32_t>(is_edge_on_X.size())) { | |
| is_edge_on_X[edge_id] = true; | |
| if (i == 0) { | |
| if (r[i] < r[i + 1]) | |
| is_X_head_edge[edge_id] |= 1; | |
| else | |
| is_X_head_edge[edge_id] |= 2; | |
| } | |
| } | |
| } | |
| if (r.size() >= 3 && max_id != -1) { | |
| const auto& r_eids = X_edge_ids[r_id]; | |
| int eid0 = r_eids[0]; | |
| if (eid0 != -1 && eid0 <= max_id) { | |
| if (r[0] < r[1]) { | |
| head_rid_fwd[eid0] = r_id; | |
| head_r2_fwd[eid0] = r[2]; | |
| } else { | |
| head_rid_rev[eid0] = r_id; | |
| head_r2_rev[eid0] = r[2]; | |
| } | |
| } | |
| int eidL = r_eids.back(); | |
| if (eidL != -1 && eidL <= max_id) { | |
| if (r[r.size() - 2] < r[r.size() - 1]) { | |
| head_rid_rev[eidL] = r_id; | |
| head_r2_rev[eidL] = r[r.size() - 3]; | |
| } else { | |
| head_rid_fwd[eidL] = r_id; | |
| head_r2_fwd[eidL] = r[r.size() - 3]; | |
| } | |
| } | |
| } | |
| } | |
| lr_cache.L_cache.init(total_edges_X * 2); | |
| lr_cache.R_cache.init(total_edges_X * 2); | |
| bcc_finder.forbidden_edge_ids = &is_edge_on_X; | |
| bcc_finder.find_BCCs(); | |
| } | |
| inline bool is_inner(Vertex u) const { | |
| return u > 0 && u < G.N && inner_r_id[u] != -1; | |
| } | |
| inline int32_t get_r_id(Vertex u) const { | |
| return (u > 0 && u < G.N) ? inner_r_id[u] : -1; | |
| } | |
| inline int32_t get_idx_in_r(Vertex u) const { | |
| return (u > 0 && u < G.N) ? inner_idx[u] : -1; | |
| } | |
| inline bool is_on_r(Vertex node, int32_t r_id) const { | |
| if (r_id < 0 || r_id >= static_cast<int32_t>(X.size())) | |
| return false; | |
| if (is_inner(node) && get_r_id(node) == r_id) | |
| return true; | |
| const Path& r = X[r_id]; | |
| return !r.empty() && (node == r.front() || node == r.back()); | |
| } | |
| inline bool edge_on_X(int32_t edge_id) const { | |
| return edge_id != -1 && | |
| edge_id < static_cast<int32_t>(is_edge_on_X.size()) && | |
| is_edge_on_X[edge_id]; | |
| } | |
| inline bool X_head(Vertex u, Vertex v, int32_t edge_id) const { | |
| if (edge_id != -1 && | |
| edge_id < static_cast<int32_t>(is_X_head_edge.size())) { | |
| if (u < v) { | |
| return (is_X_head_edge[edge_id] & 1) != 0; | |
| } else { | |
| return (is_X_head_edge[edge_id] & 2) != 0; | |
| } | |
| } | |
| return false; | |
| } | |
| inline bool check_strong_connectivity(Vertex ri, Vertex u, | |
| int32_t edge_id_ri_u, | |
| Vertex target_v) const { | |
| bool edge_in_G_prime = !edge_on_X(edge_id_ri_u); | |
| if (edge_in_G_prime) { | |
| if (edge_id_ri_u != -1 && | |
| edge_id_ri_u < | |
| static_cast<int32_t>(bcc_finder.edge_to_BCC.size())) { | |
| int32_t bcc_idx = bcc_finder.edge_to_BCC[edge_id_ri_u]; | |
| if (bcc_idx != -1 && bcc_finder.is_in_BCC(bcc_idx, target_v)) { | |
| return true; | |
| } | |
| } | |
| } else { | |
| int32_t r_prime_id = -1; | |
| Vertex r_prime_2 = V_NONE; | |
| if (edge_id_ri_u != -1 && | |
| edge_id_ri_u < static_cast<int32_t>(head_rid_fwd.size())) { | |
| if (ri < u) { | |
| r_prime_id = head_rid_fwd[edge_id_ri_u]; | |
| r_prime_2 = head_r2_fwd[edge_id_ri_u]; | |
| } else { | |
| r_prime_id = head_rid_rev[edge_id_ri_u]; | |
| r_prime_2 = head_r2_rev[edge_id_ri_u]; | |
| } | |
| } | |
| if (r_prime_id != -1 && r_prime_2 != V_NONE) { | |
| int32_t r_id = get_r_id(ri); | |
| if (r_id == -1) | |
| return false; | |
| bool r_prime_2_on_r = false; | |
| const Path& r = X[r_id]; | |
| for (Vertex v_in_r : r) { | |
| if (v_in_r == r_prime_2) { | |
| r_prime_2_on_r = true; | |
| break; | |
| } | |
| } | |
| if (r_prime_2_on_r) { | |
| if (r_prime_2 == target_v) { | |
| return true; | |
| } | |
| } else { | |
| int32_t chord_edge_id = get_edge_id(G, r_prime_2, ri); | |
| if (chord_edge_id != -1 && | |
| chord_edge_id < static_cast<int32_t>( | |
| bcc_finder.edge_to_BCC.size())) { | |
| int32_t bcc_idx = bcc_finder.edge_to_BCC[chord_edge_id]; | |
| if (bcc_idx != -1 && | |
| bcc_finder.is_in_BCC(bcc_idx, target_v)) { | |
| return true; | |
| } | |
| } | |
| } | |
| } | |
| } | |
| return false; | |
| } | |
| inline Vertex get_L(int32_t r_id, Vertex u) const { | |
| if (r_id < 0 || r_id >= (int32_t)X.size() || u <= 0 || u >= G.N) | |
| return V_NONE; | |
| PackedKey key = lr_key(r_id, u); | |
| Vertex cached_val = lr_cache.L_cache.get(key); | |
| if (cached_val != V_NONE) { | |
| return cached_val; | |
| } | |
| const Path& r = X[r_id]; | |
| Vertex L_val = V_NONE; | |
| for (int32_t i = 1; i < static_cast<int32_t>(r.size()) - 1; ++i) { | |
| Vertex ri = r[i]; | |
| pair<int64_t, int32_t> edge_info = G.get_edge_info(ri, u); | |
| int32_t current_edge_id = edge_info.second; | |
| if (current_edge_id != -1) { | |
| L_val = ri; | |
| if (i >= 2) { | |
| Vertex ri_minus_2 = r[i - 2]; | |
| if (check_strong_connectivity(ri, u, current_edge_id, | |
| ri_minus_2)) { | |
| L_val = ri_minus_2; | |
| } | |
| } | |
| break; | |
| } | |
| } | |
| lr_cache.L_cache.emplace(key, L_val); | |
| return L_val; | |
| } | |
| inline Vertex get_R(int32_t r_id, Vertex u) const { | |
| if (r_id < 0 || r_id >= (int32_t)X.size() || u <= 0 || u >= G.N) | |
| return V_NONE; | |
| PackedKey key = lr_key(r_id, u); | |
| Vertex cached_val = lr_cache.R_cache.get(key); | |
| if (cached_val != V_NONE) { | |
| return cached_val; | |
| } | |
| const Path& r = X[r_id]; | |
| Vertex R_val = V_NONE; | |
| for (int32_t i = 1; i < static_cast<int32_t>(r.size()) - 1; ++i) { | |
| Vertex ri = r[i]; | |
| pair<int64_t, int32_t> edge_info = G.get_edge_info(ri, u); | |
| int32_t current_edge_id = edge_info.second; | |
| if (current_edge_id != -1) { | |
| R_val = ri; | |
| if (i <= static_cast<int32_t>(r.size()) - 3) { | |
| Vertex ri_plus_2 = r[i + 2]; | |
| if (check_strong_connectivity(ri, u, current_edge_id, | |
| ri_plus_2)) { | |
| R_val = ri_plus_2; | |
| } | |
| } | |
| break; | |
| } | |
| } | |
| lr_cache.R_cache.emplace(key, R_val); | |
| return R_val; | |
| } | |
| }; | |
| inline Path AVOID(const Graph& G, Vertex S, Vertex T, const vector<Path>& X, | |
| const BoolVector& is_bad) { | |
| AVOID_precomputation pre(G, X); | |
| using G1Node = pair<Vertex, int>; | |
| G1Node StartNode = {S, 0}; | |
| G1Node EndNode = {T, 0}; | |
| vector<array<int64_t, 2>> dist(G.N, {INF, INF}); | |
| G1Node InvalidParent = {V_NONE, 0}; | |
| vector<array<G1Node, 2>> parent(G.N, {InvalidParent, InvalidParent}); | |
| using PQElementG1 = pair<int64_t, G1Node>; | |
| priority_queue<PQElementG1, vector<PQElementG1>, greater<PQElementG1>> pq; | |
| if (S >= G.N || T >= G.N || S == 0 || T == 0) | |
| return {}; | |
| if (!is_bad.empty() && S < static_cast<int32_t>(is_bad.size()) && is_bad[S]) | |
| return {}; | |
| dist[S][0] = 0; | |
| pq.emplace(0, StartNode); | |
| int64_t shortest_dist = INF; | |
| while (!pq.empty()) { | |
| int64_t d = pq.top().first; | |
| G1Node u_node = pq.top().second; | |
| Vertex u = u_node.first; | |
| int32_t state_u = u_node.second; | |
| pq.pop(); | |
| if (d > dist[u][state_u]) | |
| continue; | |
| if (u_node == EndNode) { | |
| shortest_dist = d; | |
| break; | |
| } | |
| int32_t r_id_u = -1; | |
| const Path* r_u_ptr = nullptr; | |
| int32_t idx_u = -1; | |
| if (state_u == 1) { | |
| if (!pre.is_inner(u)) | |
| continue; | |
| r_id_u = pre.get_r_id(u); | |
| if (r_id_u != -1) { | |
| r_u_ptr = &X[r_id_u]; | |
| idx_u = pre.get_idx_in_r(u); | |
| } | |
| } | |
| for (const auto& edge : G.adj[u]) { | |
| Vertex v = edge.to; | |
| if (v >= G.N || v == 0) | |
| continue; | |
| int64_t w = edge.weight; | |
| int32_t edge_id = edge.id; | |
| if (!is_bad.empty() && v < static_cast<int32_t>(is_bad.size()) && | |
| is_bad[v]) | |
| continue; | |
| bool v_is_inner = pre.is_inner(v); | |
| auto try_transition = [&](int32_t state_v) { | |
| int64_t new_dist = d + w; | |
| if (new_dist < dist[v][state_v]) { | |
| dist[v][state_v] = new_dist; | |
| parent[v][state_v] = u_node; | |
| pq.emplace(new_dist, G1Node{v, state_v}); | |
| } | |
| }; | |
| if (state_u == 0) { | |
| bool transition_to_high = false; | |
| if (v_is_inner) { | |
| int32_t r_id_v = pre.get_r_id(v); | |
| const Path& r_v = X[r_id_v]; | |
| int32_t idx_v = pre.get_idx_in_r(v); | |
| if ((!pre.is_on_r(u, r_id_v) && | |
| idx_v == static_cast<int32_t>(r_v.size()) - 3) || | |
| (r_v.size() > 1 && u == r_v[0] && v == r_v[1])) { | |
| transition_to_high = true; | |
| } | |
| } | |
| if (transition_to_high) { | |
| try_transition(1); | |
| } | |
| bool transition_to_low = true; | |
| if (pre.X_head(u, v, edge_id)) { | |
| transition_to_low = false; | |
| } | |
| if (v_is_inner && transition_to_low) { | |
| int32_t r_id_v = pre.get_r_id(v); | |
| if (!pre.is_on_r(u, r_id_v) && pre.get_L(r_id_v, u) == v) { | |
| transition_to_low = false; | |
| } | |
| } | |
| if (transition_to_low) { | |
| try_transition(0); | |
| } | |
| } else { | |
| if (r_u_ptr == nullptr) | |
| continue; | |
| const Path& r_u = *r_u_ptr; | |
| if (v_is_inner) { | |
| bool transition_high_high = true; | |
| int32_t r_id_v = pre.get_r_id(v); | |
| if (r_id_u == r_id_v) { | |
| if (idx_u >= 1 && v == r_u[idx_u - 1]) | |
| transition_high_high = false; | |
| if (idx_u >= 2 && v == r_u[idx_u - 2]) | |
| transition_high_high = false; | |
| } else { | |
| if (pre.get_R(r_id_u, v) == u) | |
| transition_high_high = false; | |
| } | |
| if (transition_high_high) { | |
| try_transition(1); | |
| } | |
| } | |
| bool transition_high_low = true; | |
| if (pre.edge_on_X(edge_id)) | |
| transition_high_low = false; | |
| if (idx_u >= 2 && v == r_u[idx_u - 2]) | |
| transition_high_low = false; | |
| if (!pre.is_on_r(v, r_id_u) && pre.get_R(r_id_u, v) == u) | |
| transition_high_low = false; | |
| if (v_is_inner) { | |
| int32_t r_id_v = pre.get_r_id(v); | |
| if (r_id_u != r_id_v && pre.get_L(r_id_v, u) == v) | |
| transition_high_low = false; | |
| } | |
| if (transition_high_low) { | |
| try_transition(0); | |
| } | |
| } | |
| } | |
| } | |
| if (shortest_dist == INF) | |
| return {}; | |
| Path path_G; | |
| G1Node curr = EndNode; | |
| while (curr != StartNode) { | |
| path_G.push_back(curr.first); | |
| G1Node p = parent[curr.first][curr.second]; | |
| if (p.first == V_NONE) | |
| break; | |
| curr = p; | |
| } | |
| if (curr == StartNode) | |
| path_G.push_back(StartNode.first); | |
| else | |
| return {}; | |
| reverse(path_G.begin(), path_G.end()); | |
| return path_G; | |
| } | |
| inline Path ShortestNonSeparatingPath(const Graph& G_input, Vertex S_original, | |
| Vertex T_original) { | |
| if (S_original == T_original) | |
| return {S_original}; | |
| BridgeFinder bf(G_input); | |
| bf.find_Bridges(); | |
| vector<int32_t> component_id(G_input.N, 0); | |
| int32_t comp_count = 0; | |
| vector<Vertex> stack; | |
| if (G_input.N > 1) | |
| stack.reserve(G_input.N); | |
| for (Vertex v = 1; v < G_input.N; ++v) { | |
| if (component_id[v] == 0) { | |
| comp_count++; | |
| stack.push_back(v); | |
| component_id[v] = comp_count; | |
| while (!stack.empty()) { | |
| Vertex u = stack.back(); | |
| stack.pop_back(); | |
| for (const auto& edge : G_input.adj[u]) { | |
| if (edge.id != -1 && | |
| edge.id < static_cast<int32_t>(bf.is_bridge.size()) && | |
| bf.is_bridge[edge.id]) | |
| continue; | |
| Vertex neighbor = edge.to; | |
| if (neighbor < G_input.N && component_id[neighbor] == 0) { | |
| component_id[neighbor] = comp_count; | |
| stack.push_back(neighbor); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| if (S_original >= G_input.N || T_original >= G_input.N || S_original == 0 || | |
| T_original == 0 || | |
| component_id[S_original] != component_id[T_original] || | |
| component_id[S_original] == 0) { | |
| return {}; | |
| } | |
| int32_t target_comp = component_id[S_original]; | |
| vector<Vertex> old_to_new_v(G_input.N, V_NONE); | |
| vector<Vertex> new_to_old_v; | |
| new_to_old_v.reserve(G_input.N); | |
| new_to_old_v.push_back(V_NONE); | |
| Vertex new_N_counter = 1; | |
| for (Vertex v = 1; v < G_input.N; ++v) { | |
| if (component_id[v] == target_comp) { | |
| old_to_new_v[v] = new_N_counter++; | |
| new_to_old_v.push_back(v); | |
| } | |
| } | |
| int32_t N_prime_count = new_N_counter - 1; | |
| int32_t M_prime_count = 0; | |
| vector<int32_t> degrees(N_prime_count + 1, 0); | |
| for (const auto* ptr : G_input.edge_list) { | |
| if (ptr->u < G_input.N && component_id[ptr->u] == target_comp && | |
| ptr->v < G_input.N && component_id[ptr->v] == target_comp) { | |
| M_prime_count++; | |
| Vertex u_prime = old_to_new_v[ptr->u]; | |
| Vertex v_prime = old_to_new_v[ptr->v]; | |
| if (u_prime != V_NONE) | |
| degrees[u_prime]++; | |
| if (v_prime != V_NONE) | |
| degrees[v_prime]++; | |
| } | |
| } | |
| Graph G_prime(N_prime_count, M_prime_count); | |
| Vertex S_prime = old_to_new_v[S_original]; | |
| Vertex T_prime = old_to_new_v[T_original]; | |
| for (int32_t i = 1; i <= N_prime_count; ++i) { | |
| G_prime.adj[i].reserve(degrees[i]); | |
| } | |
| int32_t new_edge_id = 0; | |
| for (const auto* ptr : G_input.edge_list) { | |
| if (ptr->u < G_input.N && component_id[ptr->u] == target_comp && | |
| ptr->v < G_input.N && component_id[ptr->v] == target_comp) { | |
| Vertex u_prime = old_to_new_v[ptr->u]; | |
| Vertex v_prime = old_to_new_v[ptr->v]; | |
| G_prime.add_edge(u_prime, v_prime, ptr->weight, new_edge_id++, | |
| nullptr); | |
| } | |
| } | |
| G_prime.finalize_construction(); | |
| const Graph& G = G_prime; | |
| Vertex S = S_prime; | |
| Vertex T = T_prime; | |
| BoolVector is_bad = compute_BAD_G(G, S, T); | |
| pair<int64_t, Path> result_P = dijkstra(G, S, T, is_bad); | |
| if (result_P.first == INF) | |
| return {}; | |
| Path P = result_P.second; | |
| vector<Path> X_ST = compute_X_ST(G, P); | |
| vector<int32_t> P_indices = get_P_indices_vec(P, G.N); | |
| vector<int32_t> forbidden_ids_vec; | |
| int32_t max_id = -1; | |
| auto find_edge_id = [&](Vertex u, Vertex v) { | |
| return get_edge_id(G, u, v); | |
| }; | |
| for (const Path& r : X_ST) { | |
| if (r.size() < 4) | |
| continue; | |
| bool is_S_sep = true; | |
| for (size_t i = 2; i < r.size(); ++i) { | |
| if (r[i] >= G.N || P_indices[r[i]] == -1) { | |
| is_S_sep = false; | |
| break; | |
| } | |
| } | |
| bool is_T_sep = true; | |
| for (size_t i = 0; i <= r.size() - 3; ++i) { | |
| if (r[i] >= G.N || P_indices[r[i]] == -1) { | |
| is_T_sep = false; | |
| break; | |
| } | |
| } | |
| if (is_S_sep) { | |
| int32_t id = find_edge_id(r[r.size() - 2], r[r.size() - 1]); | |
| if (id != -1) { | |
| forbidden_ids_vec.push_back(id); | |
| if (id > max_id) | |
| max_id = id; | |
| } | |
| } | |
| if (is_T_sep) { | |
| int32_t id = find_edge_id(r[0], r[1]); | |
| if (id != -1) { | |
| forbidden_ids_vec.push_back(id); | |
| if (id > max_id) | |
| max_id = id; | |
| } | |
| } | |
| } | |
| BoolVector G0_forbidden_edge_ids; | |
| if (max_id != -1) { | |
| G0_forbidden_edge_ids.resize(max_id + 1, false); | |
| for (int32_t id : forbidden_ids_vec) | |
| G0_forbidden_edge_ids[id] = true; | |
| } | |
| pair<int64_t, Path> result_P0 = | |
| dijkstra(G, S, T, is_bad, G0_forbidden_edge_ids); | |
| Path P0; | |
| if (result_P0.first != INF) | |
| P0 = result_P0.second; | |
| vector<Path> X_EXTRA = compute_X_EXTRA(G, P0); | |
| vector<Path> X_combined = std::move(X_ST); | |
| X_combined.reserve(X_combined.size() + X_EXTRA.size()); | |
| X_combined.insert(X_combined.end(), | |
| std::make_move_iterator(X_EXTRA.begin()), | |
| std::make_move_iterator(X_EXTRA.end())); | |
| std::sort(X_combined.begin(), X_combined.end()); | |
| X_combined.erase(std::unique(X_combined.begin(), X_combined.end()), | |
| X_combined.end()); | |
| const vector<Path>& X = X_combined; | |
| Path result_path_prime = AVOID(G, S, T, X, is_bad); | |
| if (result_path_prime.empty()) { | |
| return {}; | |
| } | |
| Path result_path_original; | |
| result_path_original.reserve(result_path_prime.size()); | |
| for (Vertex v_prime : result_path_prime) { | |
| if (v_prime > 0 && | |
| v_prime < static_cast<int32_t>(new_to_old_v.size())) { | |
| result_path_original.push_back(new_to_old_v[v_prime]); | |
| } else { | |
| return {}; | |
| } | |
| } | |
| return result_path_original; | |
| } | |
| constexpr int __buffsize = 1 << 20; | |
| char __buff[__buffsize]; | |
| char *__buffs = __buff, *__buffe = __buff; | |
| inline char getc() { | |
| if (__buffs == __buffe) { | |
| size_t flywheel = fread(__buff, 1, __buffsize, stdin); | |
| if (flywheel == 0) | |
| return EOF; | |
| __buffe = __buff + flywheel; | |
| __buffs = __buff; | |
| } | |
| return *(__buffs++); | |
| } | |
| template <typename T> inline void read_number(T& x) { | |
| x = 0; | |
| static char c; | |
| bool flag = false; | |
| while ((c = getc()) != EOF && (c < '0' || c > '9') && c != '-') | |
| ; | |
| if (c == EOF) | |
| return; | |
| if (c == '-') { | |
| flag = true; | |
| c = getc(); | |
| } else if (c >= '0' && c <= '9') { | |
| x = c - '0'; | |
| c = getc(); | |
| } | |
| while (c != EOF && c >= '0' && c <= '9') { | |
| x = (x << 3) + (x << 1) + (c - '0'); | |
| c = getc(); | |
| } | |
| if (flag) | |
| x = -x; | |
| } | |
| inline void read_int(int32_t& x) { read_number<int32_t>(x); } | |
| inline void read_long(int64_t& x) { read_number<int64_t>(x); } | |
| int main() { | |
| ios_base::sync_with_stdio(false); | |
| int32_t n, m; | |
| read_int(n); | |
| read_int(m); | |
| struct InputEdge { | |
| PackedKey key; | |
| int64_t w; | |
| bool operator<(const InputEdge& other) const { | |
| if (key != other.key) | |
| return key < other.key; | |
| return w < other.w; | |
| } | |
| }; | |
| vector<InputEdge> input_edges; | |
| if (m > 0) | |
| input_edges.reserve(m); | |
| for (int32_t i = 0; i < m; ++i) { | |
| int32_t u, v; | |
| int64_t w; | |
| read_int(u); | |
| read_int(v); | |
| read_long(w); | |
| if (u != v) { | |
| PackedKey key = edge_key(u, v); | |
| input_edges.push_back({key, w}); | |
| } | |
| } | |
| sort(input_edges.begin(), input_edges.end()); | |
| int32_t s, t; | |
| read_int(s); | |
| read_int(t); | |
| int32_t unique_edge_count = 0; | |
| vector<int32_t> degrees(n + 1, 0); | |
| if (!input_edges.empty()) { | |
| unique_edge_count = 1; | |
| auto count_degree = [&](const InputEdge& edge) { | |
| pair<Vertex, Vertex> uv = unpack_edge_key(edge.key); | |
| if (uv.first <= n) | |
| degrees[uv.first]++; | |
| if (uv.second <= n) | |
| degrees[uv.second]++; | |
| }; | |
| count_degree(input_edges[0]); | |
| for (size_t i = 1; i < input_edges.size(); ++i) { | |
| if (input_edges[i].key != input_edges[i - 1].key) { | |
| unique_edge_count++; | |
| count_degree(input_edges[i]); | |
| } | |
| } | |
| } | |
| Graph G(n, unique_edge_count); | |
| for (int32_t i = 1; i <= n && i < G.N; ++i) { | |
| G.adj[i].reserve(degrees[i]); | |
| } | |
| int32_t edge_id_counter = 0; | |
| if (!input_edges.empty()) { | |
| auto add_edge_to_G = [&](const InputEdge& edge) { | |
| pair<Vertex, Vertex> uv = unpack_edge_key(edge.key); | |
| int64_t w = edge.w; | |
| int32_t id = edge_id_counter++; | |
| G.add_edge(uv.first, uv.second, w, id, nullptr); | |
| }; | |
| add_edge_to_G(input_edges[0]); | |
| for (size_t i = 1; i < input_edges.size(); ++i) { | |
| if (input_edges[i].key != input_edges[i - 1].key) { | |
| add_edge_to_G(input_edges[i]); | |
| } | |
| } | |
| } | |
| G.finalize_construction(); | |
| Path result = ShortestNonSeparatingPath(G, s, t); | |
| if (result.empty()) { | |
| printf("-1\n"); | |
| } else { | |
| int64_t total_weight = 0; | |
| for (size_t i = 0; i < result.size() - 1; ++i) { | |
| total_weight += get_edge_length(G, result[i], result[i + 1]); | |
| } | |
| printf("%lld\n", (long long)total_weight); | |
| } | |
| return 0; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment