| # | Time | Username | Problem | Language | Result | Execution time | Memory | 
|---|---|---|---|---|---|---|---|
| 1211170 | Madhav_1608 | Commuter Pass (JOI18_commuter_pass) | C++20 | 0 ms | 0 KiB | 
#include <iostream>
#include <stack>
#include <queue>
#include <string>
#include <algorithm>
#include <vector>
#include <map>
#include <set>
#include <climits>
#include <cmath>
#include <unordered_map>
#include <unordered_set>
#include <numeric>
#include <iomanip>
#include <cstring>
#include <stdio.h>
#include <assert.h>
using namespace std;
typedef long long ll;
typedef vector<int> vi;
typedef vector<string> vs;
typedef vector<long long> vll;
typedef pair<ll,ll> pll;
#define double long double // This might be an issue if you're dealing with very precise floating points, but seems ok for typical pathfinding where weights are integers/long longs.
#define F first
#define S second
const double eps = 1e-9;
#define FOR(i,a,b) for(long long i=a;i<b;i++)
#define all(v) v.begin(),v.end()
template <typename I>
void print(vector<I> &v){
    FOR(i,0,v.size()){cout << v[i] << " ";}
    cout << "\n";
}
ll gcd(ll a,ll b){
    if(a==0){return b;}
    else if(b==0){return a;}
    else if(a<b){return gcd(b%a,a);}
    else{return gcd(a%b,b);}
}
ll lcm(ll a,ll b){
    return (a/gcd(a,b))*b;
}
// Global variable or passed by reference to avoid recreating
// Used to store edges on *any* shortest path from s to t
vector<vector<pll>> special_edges_adj;
vector<vector<pll>> special_edges_rev_adj; // For the "adj2" equivalent
// Visited array for DFS to prevent cycles in the shortest path reconstruction
vector<bool> dfs_visited;
vll dist_s_global; // Store dist from s globally or pass around
// DFS to find all edges on *any* shortest path from s to t
// This function populates special_edges_adj and special_edges_rev_adj
void build_shortest_path_edges(ll current_node, ll target_s_end, const vector<vector<pll>>& adj_original) {
    if (current_node == target_s_end) {
        return; // Reached the target
    }
    if (dfs_visited[current_node]) {
        return; // Avoid cycles or redundant processing
    }
    dfs_visited[current_node] = true;
    for (const pll& neighbor_pair : adj_original[current_node]) {
        ll neighbor = neighbor_pair.F;
        ll weight = neighbor_pair.S;
        // If the path from s_start to current_node + edge_weight + path from neighbor to t_end
        // equals the shortest path from s_start to t_end, then this edge is on a shortest path.
        // We need dist from t_end for this. Let's assume we also computed dist from t_end.
        // To precisely replicate your logic, we'd need to trace predecessors.
        // The original code's `prev` logic implicitly identified these by only considering direct predecessors on shortest paths.
        // My previous detailed response's check: dist_s[u] + w + dist_t[v] == dist_s[t_end] is more robust for finding ALL such edges.
        // To exactly mimic your prev logic, we iterate over neighbours, and if they
        // satisfy dist[current_node] + weight == dist[neighbor]
        // then neighbor is a predecessor of current_node on a shortest path.
        // Your code builds `prev[p.F].push_back(c.S)` if alt == dist[p.F].
        // This means `prev[neighbor]` would contain `current_node`.
        // So, we are going *backwards* from t to s.
        // The original code uses a queue to traverse the `prev` graph, which essentially does a BFS on the reverse of the shortest path DAG.
        // We can do this explicitly or with a direct BFS.
        // Replicating the prev-based approach:
        // We need `dist_s` (shortest distances from `s_start`) computed in `solve`.
        if (dist_s_global[current_node] != MAXN &&
            dist_s_global[neighbor] != MAXN &&
            dist_s_global[neighbor] + weight == dist_s_global[current_node]) { // current_node is a predecessor of neighbor
                                                                                 // on a shortest path from s.
                                                                                 // So, we're building the adj1 (t->s) from current_node to neighbor.
            special_edges_adj[current_node].push_back({neighbor, 0});
            special_edges_rev_adj[neighbor].push_back({current_node, 0}); // Corresponds to adj2
            if (!dfs_visited[neighbor]) { // Recurse only if not yet visited
                build_shortest_path_edges(neighbor, target_s_end, adj_original);
            }
        }
    }
    dfs_visited[current_node] = false; // Backtrack
}
ll func(vector<vector<pll>> &adj_original, vector<vector<pll>> &additional_adj, ll n, ll u_query, ll v_query){
    // This func combines original graph edges and 'additional_adj' edges
    // No need to copy 'adj_original' into a temporary 'adj' inside this function
    // Use the combined logic directly.
    vll dist(n,MAXN);
    vector<bool> visited(n,false); // A fresh visited array for each Dijkstra call
    priority_queue<pll,vector<pll>,greater<pll>> pq;
    pq.push({0,u_query});
    dist[u_query] = 0;
    while(!pq.empty()){
        pll c = pq.top();
        pq.pop();
        ll current_d = c.F;
        ll current_u = c.S;
        if(visited[current_u]) continue;
        visited[current_u] = true;
        // Iterate through original graph edges
        for(pll &p : adj_original[current_u]){
            ll alt = current_d + p.S;
            if(alt < dist[p.F]){
                dist[p.F] = alt;
                pq.push({alt,p.F});
            }
        }
        // Iterate through additional (special) edges
        for(pll &p : additional_adj[current_u]){
            ll alt = current_d + p.S; // p.S will be 0 for these special edges
            if(alt < dist[p.F]){
                dist[p.F] = alt;
                pq.push({alt,p.F});
            }
        }
    }
    return dist[v_query];
}
void solve(){
    ll n,m;
    cin >> n >> m;
    n++; // Adjust for 1-indexed nodes if present
    ll s_start, t_end;
    cin >> s_start >> t_end;
    ll u_query, v_query;
    cin >> u_query >> v_query;
    vector<vector<pll>> adj_original(n); // The base graph
    for(ll i=0; i<m; ++i){
        ll a,b,c;
        cin >> a >> b >> c;
        adj_original[a].push_back({b,c});
        adj_original[b].push_back({a,c});
    }
    // Step 1: Run Dijkstra from s_start on the original graph
    dist_s_global.assign(n, MAXN); // Initialize globally accessible dist_s
    vector<bool> visited_dijkstra(n, false); // For the first Dijkstra run
    priority_queue<pll,vector<pll>,greater<pll>> pq_s;
    pq_s.push({0,s_start});
    dist_s_global[s_start] = 0;
    while(!pq_s.empty()){
        pll c = pq_s.top();
        pq_s.pop();
        ll current_d = c.F;
        ll current_u = c.S;
        if(visited_dijkstra[current_u]) continue;
        visited_dijkstra[current_u] = true;
        for(pll &p : adj_original[current_u]){
            ll alt = current_d + p.S;
            if(alt < dist_s_global[p.F]){
                dist_s_global[p.F] = alt;
                pq_s.push({alt,p.F});
            }
            // No need to store `prev` here directly for memory optimization.
            // We'll reconstruct using dist_s_global.
        }
    }
    // Step 2: Build `adj1` and `adj2` (special_edges_adj and special_edges_rev_adj)
    // This part replaces your original `prev` vector and subsequent queue processing.
    // We'll use a DFS (or BFS) starting from `t_end` traversing backwards along shortest paths.
    special_edges_adj.assign(n, vector<pll>()); // adj1
    special_edges_rev_adj.assign(n, vector<pll>()); // adj2 (for the reverse direction)
    dfs_visited.assign(n, false); // Reset visited for the DFS traversal
    // Use a queue to simulate your original BFS-like traversal of `prev` graph
    queue<ll> q_sp;
    q_sp.push(t_end);
    vector<bool> sp_visited_q(n, false); // To prevent adding nodes multiple times to queue
    sp_visited_q[t_end] = true;
    while(!q_sp.empty()){
        ll current = q_sp.front();
        q_sp.pop();
        // Iterate through all neighbors in the original graph
        // If a neighbor 'i' is a predecessor of 'current' on an s-t shortest path
        for(const pll& edge : adj_original[current]){
            ll neighbor = edge.F;
            ll weight = edge.S;
            // Condition for `neighbor` being a predecessor of `current` on a shortest path from `s_start`:
            // dist_s[neighbor] + weight == dist_s[current]
            if (dist_s_global[neighbor] != MAXN && dist_s_global[current] != MAXN &&
                dist_s_global[neighbor] + weight == dist_s_global[current]) {
                
                // This edge (neighbor -> current) is on a shortest path.
                // It means `current` has `neighbor` as a predecessor.
                // Your original `adj1[c].push_back({i,0})` corresponds to `adj1[current].push_back({neighbor,0})`
                special_edges_adj[current].push_back({neighbor, 0});
                
                // Your original `adj2[j.F].push_back({i,0})` corresponds to `adj2[neighbor].push_back({current,0})`
                special_edges_rev_adj[neighbor].push_back({current, 0});
                if (!sp_visited_q[neighbor]) {
                    sp_visited_q[neighbor] = true;
                    q_sp.push(neighbor);
                }
            }
        }
    }
    // Step 3: Call func for both scenarios (adj1 and adj2)
    // func(adj, adj1, n, u, v)  --> func(adj_original, special_edges_adj, n, u_query, v_query)
    // func(adj, adj2, n, u, v)  --> func(adj_original, special_edges_rev_adj, n, u_query, v_query)
    cout << min(func(adj_original, special_edges_adj, n, u_query, v_query),
                func(adj_original, special_edges_rev_adj, n, u_query, v_query)) << "\n";
}
signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    // init_code(); // This line is commented out, so it won't affect behavior
    ll t=1;
    // cin >> t; // Only one test case as per comments
    while(t--){
        solve();
    }
    return 0;
}
