結果
| 問題 | No.1212 Second Path |
| コンテスト | |
| ユーザー |
qwewe
|
| 提出日時 | 2025-05-14 13:04:28 |
| 言語 | C++17 (gcc 13.3.0 + boost 1.87.0) |
| 結果 |
TLE
|
| 実行時間 | - |
| コード長 | 9,493 bytes |
| コンパイル時間 | 2,394 ms |
| コンパイル使用メモリ | 110,664 KB |
| 実行使用メモリ | 38,104 KB |
| 最終ジャッジ日時 | 2025-05-14 13:06:21 |
| 合計ジャッジ時間 | 11,790 ms |
|
ジャッジサーバーID (参考情報) |
judge5 / judge3 |
(要ログイン)
| ファイルパターン | 結果 |
|---|---|
| sample | -- * 3 |
| other | TLE * 1 -- * 44 |
ソースコード
#include <iostream>
#include <vector>
#include <queue>
#include <algorithm>
#include <map> // Using map to store edges on the path for quick lookup
#include <vector> // Standard vectors usage
using namespace std;
// Define long long for large edge weights and path lengths
typedef long long ll;
// Define a large value for infinity, suitable for sums of edge weights up to 10^9
// N <= 10^5, max path length could be N-1. Path length * 2 could exceed 32-bit int.
// A path could have length up to (N-1)*10^9. Plus detour 2*10^9. Still fits in 64-bit ll.
// Use a value larger than any possible path length. Max path length roughly 10^5 * 10^9 = 10^14.
// Add detour max 2*10^9. So result fits in ll. INF needs to be larger. 4e18 is safe.
const ll INF = 4e18;
// Structure to represent an edge in the adjacency list
struct Edge {
int to; // Neighbor vertex
ll weight; // Edge weight
};
vector<vector<Edge>> adj; // Adjacency list representation of the tree
// Variables needed for LCA (Lowest Common Ancestor) calculation using binary lifting
vector<int> parent; // parent[u] stores the parent of node u in the BFS tree from root
vector<ll> dist_from_root; // dist_from_root[u] stores the distance from root (node 1) to node u
vector<int> depth; // depth[u] stores the depth of node u (distance in edges from root)
vector<vector<int>> ancestor; // ancestor[k][u] stores the 2^k-th ancestor of node u
int MAX_LOG_N; // Maximum power of 2 needed for binary lifting, depends on N
// Performs BFS starting from a given root to compute depths, parents, and distances
// Also preprocesses the first level parents for binary lifting LCA
void bfs_lca(int root, int N) {
// Initialize vectors
depth.assign(N + 1, -1); // -1 indicates unvisited
parent.assign(N + 1, 0); // Parent of root is 0 (pseudo node)
dist_from_root.assign(N + 1, 0); // Distance from root to itself is 0
depth[root] = 0; // Root is at depth 0
queue<int> q;
q.push(root);
depth[0] = -1; // Set depth of pseudo parent node 0 to -1
// Standard BFS traversal
while (!q.empty()) {
int u = q.front();
q.pop();
for (const auto& edge : adj[u]) {
int v = edge.to;
if (depth[v] == -1) { // If neighbor v is not visited
depth[v] = depth[u] + 1; // Set depth
parent[v] = u; // Set parent
dist_from_root[v] = dist_from_root[u] + edge.weight; // Update distance from root
q.push(v); // Add neighbor to queue
}
}
}
// Precompute ancestors for binary lifting LCA
MAX_LOG_N = 0;
while ((1 << MAX_LOG_N) <= N) {
MAX_LOG_N++;
}
// Resize ancestor table: MAX_LOG_N levels, N+1 nodes
ancestor.assign(MAX_LOG_N, vector<int>(N + 1, 0));
// Base case: 2^0 = 1st ancestor is the direct parent
for (int i = 1; i <= N; ++i) {
ancestor[0][i] = parent[i];
}
// Dynamically compute 2^k-th ancestors using 2^(k-1)-th ancestors
for (int k = 1; k < MAX_LOG_N; ++k) {
for (int i = 1; i <= N; ++i) {
// Check if the 2^(k-1)-th ancestor exists
if (ancestor[k - 1][i] != 0) {
// The 2^k-th ancestor is the 2^(k-1)-th ancestor of the 2^(k-1)-th ancestor
ancestor[k][i] = ancestor[k - 1][ancestor[k - 1][i]];
}
}
}
}
// Computes the Lowest Common Ancestor (LCA) of two nodes u and v using binary lifting
int lca(int u, int v) {
// Ensure u is the deeper node, swap if needed
if (depth[u] < depth[v]) {
swap(u, v);
}
// Lift node u up until it's at the same depth as node v
for (int k = MAX_LOG_N - 1; k >= 0; --k) {
// Check if 2^k-th ancestor exists and lifting doesn't overshoot v's depth
if (ancestor[k][u] != 0 && depth[u] - (1 << k) >= depth[v]) {
u = ancestor[k][u]; // Lift u
}
}
// If u and v are now the same node, it's the LCA
if (u == v) {
return u;
}
// Lift u and v simultaneously maintaining the property that their parents might be LCA
for (int k = MAX_LOG_N - 1; k >= 0; --k) {
// If their 2^k-th ancestors are different, lift both
if (ancestor[k][u] != ancestor[k][v]) {
u = ancestor[k][u];
v = ancestor[k][v];
}
}
// After the loop, u and v are children of the LCA. Return parent of u (or v).
return parent[u];
}
// Computes the distance between nodes u and v using their distances from root and LCA
ll get_dist(int u, int v) {
int l = lca(u, v); // Find LCA
// Distance formula: dist(u, v) = dist(root, u) + dist(root, v) - 2 * dist(root, lca(u, v))
return dist_from_root[u] + dist_from_root[v] - 2 * dist_from_root[l];
}
// Global list to store nodes on the current query path
vector<int> path_nodes_list;
// Map to store edges on the current query path for O(log N) or O(1) avg lookup
// Key is pair {min(u,v), max(u,v)} representing an undirected edge
map<pair<int, int>, bool> on_path_edge_map;
// Helper function to reconstruct path segment from node u up to target_ancestor (exclusive)
// Adds nodes to path_nodes_list and edges to on_path_edge_map
void find_path_info(int u, int target_ancestor) {
int curr = u;
while (curr != target_ancestor) { // Keep climbing until target ancestor is reached
path_nodes_list.push_back(curr); // Add current node to path list
int p = parent[curr]; // Get parent
if (p != 0) { // Check if parent is valid (not pseudo node 0)
// Store edge {curr, p} in map using canonical representation {min, max}
int u1 = min(curr, p), v1 = max(curr, p);
on_path_edge_map[{u1, v1}] = true;
}
curr = p; // Move up to parent
if(curr == 0) break; // Safety break: should not happen in a connected tree with nodes 1..N
}
}
int main() {
// Use fast I/O operations
ios_base::sync_with_stdio(false);
cin.tie(NULL);
int N; // Number of vertices
cin >> N;
adj.resize(N + 1); // Resize adjacency list
// Read N-1 edges
for (int i = 0; i < N - 1; ++i) {
int u, v;
ll w;
cin >> u >> v >> w;
// Add edge to adjacency list for both directions (undirected graph)
adj[u].push_back({v, w});
adj[v].push_back({u, w});
}
// Precompute LCA related structures (depth, parent, distance, ancestor table)
bfs_lca(1, N); // Root the tree arbitrarily at node 1
int Q; // Number of queries
cin >> Q;
// Pre-allocate space for path nodes list for potential efficiency gain
path_nodes_list.reserve(N);
// Process each query
while (Q--) {
int x, y; // Start and end vertices for the query
cin >> x >> y;
// Calculate the shortest path distance between x and y
ll shortest_dist = get_dist(x, y);
// Clear data structures from previous query
path_nodes_list.clear();
on_path_edge_map.clear();
// Find the LCA of x and y
int l = lca(x, y);
// Reconstruct the simple path from x to LCA and mark nodes/edges
find_path_info(x, l);
// Reconstruct the simple path from y to LCA and mark nodes/edges
find_path_info(y, l);
// Add the LCA node itself to the list of path nodes
path_nodes_list.push_back(l);
// Variable to store the minimum added length for the second shortest path
ll min_added_len = INF;
// Map to keep track of nodes already processed to avoid redundant checks
map<int, bool> processed_nodes;
// Iterate through each unique node u on the simple path P_xy
for (int u : path_nodes_list) {
// Skip node 0 (if ever appears) or nodes already processed
if (u == 0 || processed_nodes[u]) continue;
processed_nodes[u] = true; // Mark node u as processed
// Iterate through all edges incident to node u
for (const auto& edge : adj[u]) {
int v = edge.to; // Neighbor vertex
ll w = edge.weight; // Edge weight
// Canonical representation of the edge {u, v}
int u1 = min(u, v), v1 = max(u, v);
// Check if this edge {u, v} is part of the simple path P_xy
if (on_path_edge_map.find({u1, v1}) == on_path_edge_map.end()) {
// If the edge is NOT on the simple path P_xy:
// It can be used for a detour u -> v -> u.
// This adds 2 * w to the total path length.
// Update the minimum added length found so far.
min_added_len = min(min_added_len, 2 * w);
}
}
}
// Output the result for the query
if (min_added_len == INF) {
// If min_added_len is still INF, it means no valid detour edge was found.
// This typically happens if the simple path P_xy uses all edges connected to its nodes.
// E.g., the tree is just a path graph and x, y are its endpoints.
cout << -1 << "\n";
} else {
// The second shortest path length is the shortest path length plus the minimum detour cost.
cout << shortest_dist + min_added_len << "\n";
}
}
return 0;
}
qwewe