Search for a command to run...
Progressive hints first, then the full explanation and implementation when you're ready to cash out.
Review status
AI-generated and still unreviewed. Double-check the details before internalizing them.
Hints
Open only as much as you need to keep the solve alive.
A lane is “definitely used” only if every path from to has to cross it. So don’t think about one chosen route. Think about edges that you literally cannot bypass.
In an undirected graph, the only edges that can be unavoidable are bridges. If an edge belongs to a cycle, you can go around that cycle, so it cannot be forced. Cycles are the graph saying “nice try, but no.”
Not every bridge matters. A bridge is useful only if removing it separates vertex from vertex . During a DFS bridge search, root the DFS tree at . For a tree edge that is a bridge, it separates from exactly the subtree of ; it is required iff lies in that subtree.
Once you know all required lanes, each query asks for the closest required edge to vertex , where an edge is distance from both endpoints. That screams multi-source shortest paths: put both endpoints of every required edge into the source set.
The tie-break is by smallest lane index among equally close required lanes. Run Dijkstra/BFS over labels lexicographically, initializing both endpoints of every required edge with . Then each query answer is just the stored edge index for that vertex.
The Smeshariki can take any path from to . We need the lanes that appear in all such paths.
That wording is the whole problem. It is not asking about shortest paths. It is not asking about one chosen DFS route. It is asking for edges that are impossible to avoid.
Which edges are definitely used?
Call an edge required if every path from to uses it.
First, any required edge must be a bridge.
If an edge lies on a cycle, then after entering one endpoint of the edge, you can go around the rest of the cycle and reach the other endpoint without using that edge. So that edge is bypassable. Bypassable means not guaranteed. Dead.
So required edges are among the bridges.
But not every bridge is required. A bridge somewhere in a side branch does not matter if all paths from to stay outside that branch.
For a bridge , removing it splits the graph into two connected components. The bridge is required exactly when vertex is in one component and vertex is in the other. Then every path must cross the bridge, because there is no other connection between the two sides.
So the first part is:
Finding the relevant bridges
Run the standard Tarjan DFS for bridges, rooted at vertex .
For every vertex :
For a DFS tree edge , it is a bridge iff:
Because then the subtree of has no back edge reaching or anything above .
Since the DFS is rooted at , removing a bridge edge separates the graph into:
So this bridge separates and iff is inside the subtree of .
We can check that with exit times. During DFS, compute after finishing the subtree. Then is in the subtree of iff:
Thus, for every bridge tree edge , it is required iff:
That gives the exact set of unavoidable lanes.
If this set is empty, every query answer is .
Answering nearest required lane queries
Now suppose we have all required edges.
For a query vertex , we need the required edge minimizing:
for edge .
That is exactly the shortest distance from to either endpoint of that edge.
So treat every endpoint of every required edge as a source. If required edge connects , initialize both and with label:
For every vertex, we want the best label:
The distance part is normal shortest paths in an unweighted graph. The index part is the tie-break.
The cleanest way is to run Dijkstra with lexicographic labels :
All graph edges have weight , so this is basically BFS with a tie-break, but Dijkstra avoids fiddly queue-order bugs. At , it is easily fast enough.
When relaxing an edge from to , the candidate label is:
Update if that pair is lexicographically better.
After that, every query is answered in by printing .
Why this is correct
A lane is required iff every path uses it.
If a required lane were not a bridge, then after removing it the graph would still keep its endpoints connected through another route. Any path using that lane could replace the lane by the alternate route, producing a path that avoids it. Contradiction. So every required lane is a bridge.
For a bridge, removing it splits the graph into two components. If and are on the same side, then there is a path entirely inside that side, so the bridge is not required. If they are on opposite sides, every path from to must cross between the two components, and the bridge is the only edge doing that. So it is required.
The DFS bridge test finds exactly the bridges. Rooting at lets us test whether is in the child subtree of each bridge, which is exactly the “opposite sides” condition.
For queries, the distance from a vertex to an edge is the minimum distance to either endpoint. Initializing both endpoints of each required edge as sources with distance makes the shortest-path label at vertex equal to the minimum distance from to any required edge. Lexicographic Dijkstra preserves the smallest edge index among equal-distance choices, so the stored edge index is exactly what the statement asks for.
Complexity
For each test case:
The total input size is , so this fits comfortably. No black magic, just bridges and shortest paths doing their jobs.
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void setIO() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
}
int main() {
setIO();
int T;
cin >> T;
while (T--) {
int n, m;
cin >> n >> m;
vector<pair<int, int>> edges(m + 1);
vector<vector<pair<int, int>>> g(n + 1);
for (int id = 1; id <= m; id++) {
int u, v;
cin >> u >> v;
edges[id] = {u, v};
g[u].push_back({v, id});
g[v].push_back({u, id});
}
vector<int> tin(n + 1), low(n + 1), tout(n + 1), isRequired(m + 1, 0);
int timer = 0;
auto is_ancestor = [&](int anc, int v) {
return tin[anc] <= tin[v] && tin[v] <= tout[anc];
};
function<void(int, int)> dfs = [&](int v, int parentEdge) {
tin[v] = low[v] = ++timer;
for (auto [to, id] : g[v]) {
if (id == parentEdge) continue;
if (tin[to]) {
low[v] = min(low[v], tin[to]);
} else {
dfs(to, id);
low[v] = min(low[v], low[to]);
if (low[to] > tin[v] && is_ancestor(to, n)) {
isRequired[id] = 1;
}
}
}
tout[v] = timer;
};
dfs(1, 0);
const int INF = 1e9;
vector<int> dist(n + 1, INF), best(n + 1, INF);
using State = tuple<int, int, int>;
priority_queue<State, vector<State>, greater<State>> pq;
for (int id = 1; id <= m; id++) {
if (!isRequired[id]) continue;
auto [u, v] = edges[id];
if (make_pair(0, id) < make_pair(dist[u], best[u])) {
dist[u] = 0;
best[u] = id;
pq.push({0, id, u});
}
if (make_pair(0, id) < make_pair(dist[v], best[v])) {
dist[v] = 0;
best[v] = id;
pq.push({0, id, v});
}
}
while (!pq.empty()) {
auto [d, id, v] = pq.top();
pq.pop();
if (make_pair(d, id) != make_pair(dist[v], best[v])) continue;
for (auto [to, edgeId] : g[v]) {
pair<int, int> cand = {d + 1, id};
pair<int, int> cur = {dist[to], best[to]};
if (cand < cur) {
dist[to] = cand.first;
best[to] = cand.second;
pq.push({dist[to], best[to], to});
}
}
}
int q;
cin >> q;
while (q--) {
int c;
cin >> c;
cout << (best[c] == INF ? -1 : best[c]) << ' ';
}
cout << '\n';
}
return 0;
}#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void setIO() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
}
int main() {
setIO();
int T;
cin >> T;
while (T--) {
int n, m;
cin >> n >> m;
vector<pair<int, int>> edges(m + 1);
vector<vector<pair<int, int>>> g(n + 1);
for (int id = 1; id <= m; id++) {
int u, v;
cin >> u >> v;
edges[id] = {u, v};
g[u].push_back({v, id});
g[v].push_back({u, id});
}
vector<int> tin(n + 1), low(n + 1), tout(n + 1), isRequired(m + 1, 0);
int timer = 0;
auto is_ancestor = [&](int anc, int v) {
return tin[anc] <= tin[v] && tin[v] <= tout[anc];
};
function<void(int, int)> dfs = [&](int v, int parentEdge) {
tin[v] = low[v] = ++timer;
for (auto [to, id] : g[v]) {
if (id == parentEdge) continue;
if (tin[to]) {
low[v] = min(low[v], tin[to]);
} else {
dfs(to, id);
low[v] = min(low[v], low[to]);
if (low[to] > tin[v] && is_ancestor(to, n)) {
isRequired[id] = 1;
}
}
}
tout[v] = timer;
};
dfs(1, 0);
const int INF = 1e9;
vector<int> dist(n + 1, INF), best(n + 1, INF);
using State = tuple<int, int, int>;
priority_queue<State, vector<State>, greater<State>> pq;
for (int id = 1; id <= m; id++) {
if (!isRequired[id]) continue;
auto [u, v] = edges[id];
if (make_pair(0, id) < make_pair(dist[u], best[u])) {
dist[u] = 0;
best[u] = id;
pq.push({0, id, u});
}
if (make_pair(0, id) < make_pair(dist[v], best[v])) {
dist[v] = 0;
best[v] = id;
pq.push({0, id, v});
}
}
while (!pq.empty()) {
auto [d, id, v] = pq.top();
pq.pop();
if (make_pair(d, id) != make_pair(dist[v], best[v])) continue;
for (auto [to, edgeId] : g[v]) {
pair<int, int> cand = {d + 1, id};
pair<int, int> cur = {dist[to], best[to]};
if (cand < cur) {
dist[to] = cand.first;
best[to] = cand.second;
pq.push({dist[to], best[to], to});
}
}
}
int q;
cin >> q;
while (q--) {
int c;
cin >> c;
cout << (best[c] == INF ? -1 : best[c]) << ' ';
}
cout << '\n';
}
return 0;
}