Problem:
Bridges

Find bridges of an undirected graph G(V, E).

A bridge (also known as a cut-edge) is an edge whose deletion increase the number of connected components.

Input

|V| |E|
s0 t0
s1 t1
:
s|E|-1 t|E|-1

, where |V| is the number of nodes and |E| is the number of edges in the graph. The graph nodes are named with the numbers 0, 1,..., |V|-1 respectively.

si and ti represent source and target nodes of i-th edge (undirected).

Output

A list of bridges of the graph ordered by name. For each bridge, names of its end-ponints, source and target (source < target), should be printed separated by a space. The sources should be printed ascending order, then the target should also be printed ascending order for the same source.

Constraints

1 ≤ |V| ≤ 100,000

0 ≤ |E| ≤ 100,000

The graph is connected

There are no parallel edges

There are no self-loops

Sample Input 1

4 4
0 1
0 2
1 2
2 3

Sample Output 1

2 3

Sample Input 2

5 4
0 1
1 2
2 3
3 4

Sample Output 2

0 1
1 2
2 3
3 4

Solution in Rust (judge verdict: Accepted)

fn solve(edges: Vec<Vec<usize>>, nodes: usize) {
    let mut is_used: Vec<bool> = vec![false; nodes];
    let mut lowlink: Vec<usize> = vec![1000000000; nodes];
    let mut orders: Vec<usize> = vec![0; nodes];
    let mut order = 0;
    let mut is_dfs_edge: Vec<_> = edges.iter().map(|es| vec![false; es.len()]).collect();
    let mut dfs_stack = vec![];
    let mut bridges = vec![];
    fn min(x: usize, y: usize) -> usize {
        if x < y {
            x
        } else {
            y
        }
    }
    fn max(x: usize, y: usize) -> usize {
        if x < y {
            y
        } else {
            x
        }
    }

    for i in 0..nodes {
        if is_used[i] {
            continue;
        }
        dfs_stack.push(i);
        // Root node
        'outer: while !dfs_stack.is_empty() {
            let node = *dfs_stack.last().unwrap();
            if !is_used[node] {
                is_used[node] = true;
                orders[node] = order;
                lowlink[node] = order;
                order += 1;
            }
            for (idx, &to) in edges[node].iter().enumerate() {
                if !is_used[to] {
                    // DFS.
                    dfs_stack.push(to);
                    is_dfs_edge[node][idx] = true;
                    continue 'outer;
                }
            }
            let last = dfs_stack.pop().unwrap();
            let parent = *dfs_stack.last().unwrap_or(&10000000);
            for (&to, &is_dfs) in edges[last].iter().zip(is_dfs_edge[node].iter()) {
                if to == parent {
                    continue;
                } else if is_dfs {
                    let low = lowlink[to];
                    lowlink[last] = min(lowlink[last], low);
                } else {
                    lowlink[last] = min(lowlink[last], orders[to]);
                }
                if orders[last] < lowlink[to] {
                    let from = min(last, to);
                    let to = max(last, to);
                    bridges.push((from, to));
                }
            }
        }
    }
    bridges.sort();
    for (s, t) in bridges {
        println!("{} {}", s, t);
    }
}

fn main() {
    use std::io::{BufRead, BufReader};
    let input = std::io::stdin();
    let input: Vec<_> = BufReader::new(input.lock())
        .lines()
        .filter_map(|e| e.ok())
        .collect();
    let (nodes, _num_edges) = {
        let x: Vec<usize> = input[0]
            .split_whitespace()
            .filter_map(|e| e.parse().ok())
            .collect();
        (x[0], x[1])
    };
    let mut edges: Vec<Vec<_>> = vec![vec![]; nodes];
    for line in input[1..].iter() {
        let edge: Vec<usize> = line
            .split_whitespace()
            .filter_map(|e| e.parse().ok())
            .collect();
        edges[edge[0]].push(edge[1]);
        edges[edge[1]].push(edge[0]);
    }
    solve(edges, nodes);
}