Submission #1211170

#TimeUsernameProblemLanguageResultExecution timeMemory
1211170Madhav_1608Commuter Pass (JOI18_commuter_pass)C++20
Compilation error
0 ms0 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; }

Compilation message (stderr)

commuter_pass.cpp: In function 'void build_shortest_path_edges(ll, ll, const std::vector<std::vector<std::pair<long long int, long long int> > >&)':
commuter_pass.cpp:93:44: error: 'MAXN' was not declared in this scope
   93 |         if (dist_s_global[current_node] != MAXN &&
      |                                            ^~~~
commuter_pass.cpp: In function 'll func(std::vector<std::vector<std::pair<long long int, long long int> > >&, std::vector<std::vector<std::pair<long long int, long long int> > >&, ll, ll, ll)':
commuter_pass.cpp:114:16: error: 'MAXN' was not declared in this scope
  114 |     vll dist(n,MAXN);
      |                ^~~~
commuter_pass.cpp: In function 'void solve()':
commuter_pass.cpp:170:29: error: 'MAXN' was not declared in this scope
  170 |     dist_s_global.assign(n, MAXN); // Initialize globally accessible dist_s
      |                             ^~~~