#include <bits/stdc++.h>
using namespace std;
using Int = int_fast64_t;

class edge{
public:
	Int cost;
	size_t from, to;
	edge(Int c, size_t f, size_t t){
		cost = c;
		from = f;
		to = t;
	}
};
using edges = vector<edge>;
using graph = vector<edges>;
void readgraph(graph &g, size_t E, bool D, bool C, bool Z){ // E:num_of_edges, D:directed, C:costed, Z:already 0-indexed
	for(size_t i=0; i<E; ++i){
		edge e(1, 0, 0);
		if(!C) cin >> e.from >> e.to;
		else cin >> e.from >> e.to >> e.cost;
		if(!Z){
			--e.from;
			--e.to;
		}
		g[e.from].push_back(e);
		if(!D){
			swap(e.from, e.to);
			g[e.from].push_back(e);
		}
	}
}
class tree{
public:
	Int n;
	Int root;
	graph g;
	vector<Int> parent;
	vector<vector<Int>> child;

	tree(Int _n) : n(_n) {root = -1; parent.resize(n, -1); child.resize(n);}
	tree(Int r, vector<Int> p) : n(p.size()), root(r), parent(p) {}
	tree(graph _g) : n(_g.size()), g(_g) {}

	void set_root(Int r){
		root = r;
		parent.resize(n, -1);
		parent[r] = r;
		child.resize(n, vector<Int>());
		queue<Int> q;
		q.push(r);
		while(!q.empty()){
			Int p = q.front(); q.pop();
			for(auto i:g[p]){
				Int t = i.to;
				if(parent[t] == -1){
					parent[t] = p;
					child[p].push_back(t);
					q.push(t);
				}
			}
		}
	}
	void find_children(Int r){
		root = r;
		child.resize(n, vector<Int>());
		for(int i=0; i<n; ++i)
			if(parent[i] != i)
				child[parent[i]].push_back(i);
	}
};
class graph_util{
public:
	static void rec_dfs(graph &g, tree &t, vector<Int> &ord, vector<Int> &low, Int &cnt, Int v, Int p){
		t.parent[v] = p;
		ord[v] = cnt;
		++cnt;
		low[v] = ord[v];
		for(auto i:g[v]){
			Int to = i.to;
			if(ord[to] == -1){
				rec_dfs(g, t, ord, low, cnt, to, v);
				low[v] = min(low[v], low[to]);
			}else if(p != to){
				low[v] = min(low[v], ord[to]);
			}
		}
	}
	static tree build_dfs_tree(graph &g, Int s, vector<Int> &ord, vector<Int> &low){
		tree t(g.size());
		ord.resize(g.size(), -1);
		low.resize(g.size(), g.size());
		Int cnt = 0;
		rec_dfs(g, t, ord, low, cnt, s, s);
		t.find_children(s);
		return t;
	}
	static vector<Int> find_articulation_point(tree &dfs_tree, vector<Int> &ord, vector<Int> &low){
		vector<Int> res;
		if((Int)dfs_tree.child[dfs_tree.root].size() > 1) res.push_back(dfs_tree.root);
		for(Int i=0; i<(Int)ord.size(); ++i){
			if(i == dfs_tree.root) continue;
			for(auto j:dfs_tree.child[i]){
				if(ord[i] <= low[j]){
					res.push_back(i);
					break;
				}
			}
		}
		return res;
	}
	static vector<pair<Int, Int>> find_bridge(tree &dfs_tree, vector<Int> &ord, vector<Int> &low){
		vector<pair<Int, Int>> res;
		for(Int i=0; i<(Int)ord.size(); ++i){
			if(i == dfs_tree.root) continue;
			Int p = dfs_tree.parent[i];
			if(ord[p] < low[i]) res.push_back(make_pair(p, i));
		}
		return res;
	}
};

int main(){
	cin.tie(0);
	ios::sync_with_stdio(false);
	Int v, e; cin >> v >> e;
	graph g(v);
	readgraph(g, e, false, false, true);
	vector<Int> ord, low;
	tree dfs_tree(graph_util::build_dfs_tree(g, 0, ord, low));
	vector<pair<Int, Int>> bridges(graph_util::find_bridge(dfs_tree, ord, low));
	for(auto &i:bridges) if(i.first > i.second) swap(i.first, i.second);
	sort(bridges.begin(), bridges.end());
	for(auto i:bridges) cout << i.first << " " << i.second << "\n";
}


Measured memory_bytes: 7532