#include <bits/stdc++.h>
#define show(x) cerr << #x << " = " << x << endl
using namespace std;
using ll = long long;
using ld = long double;
constexpr ll MOD = 1000000007LL;
template <typename T>
constexpr T INF = numeric_limits<T>::max() / 10;
template <typename Functor>
struct fix_type
{
    Functor functor;
    template <typename... Args>
    decltype(auto) operator()(Args&&... args) const& { return functor(functor, std::forward<Args>(args)...); }
};
template <typename Functor>
fix_type<typename std::decay<Functor>::type> fix(Functor&& functor) { return {std::forward<Functor>(functor)}; }



struct Graph
{
    Graph(const int v)
        : V{v}, edge(v), rev_edge(v) {}
    void addEdge(const int from, const int to)
    {
        edge[from].push_back(to);
        rev_edge[to].push_back(from);
    }
    const int V;
    vector<vector<int>> edge;
    vector<vector<int>> rev_edge;
};



class BiconnectedComponent
{
public:
    // g: 無向グラフ
    BiconnectedComponent(const Graph& g_) : size{g_.V}, ord(size, -1), low(size, 0), comp(size, -1)
    {
        const auto is_bridge = [&](const int i, const int j) { return (ord[i] < ord[j]) ? ord[i] < low[j] : ord[j] < low[i]; };
        const auto brec = fix([&](auto&& self, const int s, const int par) -> void {
            ord[s] = low[s] = num,
            num++;
            for (const int to : g_.edge[s]) {
                if (to == par) { continue; }
                if (ord[to] >= 0) {
                    low[s] = min(low[s], ord[to]);
                } else {
                    self(self, to, s);
                    low[s] = min(low[s], low[to]);
                }
                if (is_bridge(s, to)) { bridge.push_back({s, to}); }
            }
        });
        const auto crec = fix([&](auto&& self, const int s) -> void {
            comp[s] = comp_num;
            for (const int to : g_.edge[s]) {
                if (comp[to] >= 0 or is_bridge(s, to)) { continue; }
                self(self, to);
            }
        });
        for (int i = 0; i < size; i++) {
            if (ord[i] >= 0) { continue; }
            brec(i, -1);
        }
        for (int i = 0; i < size; i++) {
            if (comp[i] >= 0) { continue; }
            crec(i);
            comp_num++;
        }
    }

    Graph toTree() const
    {
        Graph tree(comp_num);
        for (const auto& p : bridge) {
            tree.addEdge(comp[p.first], comp[p.second]);
            tree.addEdge(comp[p.second], comp[p.first]);
        }
        return tree;
    }
    const vector<pair<int, int>>& getEdge() const { return bridge; }
    bool isBridge(const int i, const int j) const { return (ord[i] < ord[j]) ? ord[i] < low[j] : ord[j] < low[i]; }
    const vector<int>& getComp() const { return comp; }
    const vector<pair<int, int>>& getBridge() const { return bridge; }

private:
    int num = 0;
    int comp_num = 0;
    vector<pair<int, int>> bridge;
    const int size;
    vector<int> ord;
    vector<int> low;
    vector<int> comp;
};


// Problem: http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=GRL_3_B
// Status: Accepted

int main()
{
    int V, E;
    cin >> V >> E;
    Graph g(V);
    using P = pair<int, int>;
    vector<P> edge;
    for (int i = 0; i < E; i++) {
        int s, t;
        cin >> s >> t;
        if (s > t) { swap(s, t); }
        g.addEdge(s, t);
        g.addEdge(t, s);
        edge.push_back(make_pair(s, t));
    }

    BiconnectedComponent bic(g);
    vector<P> b = bic.getBridge();
    for (auto& p : b) {
        if (p.first > p.second) { swap(p.first, p.second); }
    }
    sort(b.begin(), b.end());
    for (const auto& p : b) { cout << p.first << " " << p.second << endl; }
    return 0;
}



Measured memory_bytes: 5900