#include #include // #include // using Bint = boost::multiprecision::cpp_int; #define REP(i, n) for(int i = 0;i < n;i++) #define ll long long using namespace std; typedef pair P; typedef pair LP; const int dx[8] = {1, 0, -1, 0, 1, -1, -1, 1}; const int dy[8] = {0, 1, 0, -1, 1, 1, -1, -1}; const int INF = 1000000000; const ll LINF = 1000000000000000000;//1e18 const double PI = acos(-1.0); const double EPS = 1e-10; template inline bool chmax(T& a, T b) { if (a < b) { a = b; return true; } return false; } template inline bool chmin(T& a, T b) { if (a > b) { a = b; return true; } return false; } inline constexpr ll ceil_div(const ll a, const ll b) {return (a + b - 1) / b - !!((a + b - 1) % b < 0);}// return ceil(a/b) inline constexpr ll floor_div(const ll a, const ll b) {return a / b - !!(a % b < 0);}// return floor(a/b) /* ----------------------- DEBUG FUNCTION ---------------------------- */ #define DUMPOUT cerr void dump_function() { DUMPOUT << ' '; } void dump_function(bool a) { DUMPOUT << a; } void dump_function(int a) { DUMPOUT << a; } void dump_function(long long a) { DUMPOUT << a; } void dump_function(char a) { DUMPOUT << a; } void dump_function(string &a) { DUMPOUT << a; } void dump_function(double a) { DUMPOUT << a; } template void dump_function(const vector &); template void dump_function(const array &); template void dump_function(const pair &p); template void dump_function(const T (&)[size]); template void dump_function(const vector &a) { if(a.empty()) return; dump_function(a[0]); for(auto i = a.begin(); ++i != a.end();) { DUMPOUT << " "; dump_function(*i); } DUMPOUT << endl; } template void dump_function(const deque &a) { if(a.empty()) return; dump_function(a[0]); for(auto i = a.begin(); ++i != a.end();) { DUMPOUT << " "; dump_function(*i); } } template void dump_function(const array &a) { dump_function(a[0]); for(auto i = a.begin(); ++i != a.end();) { DUMPOUT << " "; dump_function(*i); } } template void dump_function(const pair &p) { DUMPOUT << '('; dump_function(p.first); DUMPOUT << ","; dump_function(p.second); DUMPOUT << ')'; } template void dump_function(set &x) { for(auto e : x) dump_function(e), DUMPOUT << " "; DUMPOUT << endl; } template void dump_function(multiset &x) { for(auto e : x) dump_function(e), DUMPOUT << " "; DUMPOUT << endl; } template void dump_function(const T (&a)[size]) { dump_function(a[0]); for(auto i = a; ++i != end(a);) { DUMPOUT << " "; dump_function(*i); } } template void dump_function(const T &a) { DUMPOUT << a; } int dump_out() { DUMPOUT << '\n'; return 0; } template int dump_out(const T &t) { dump_function(t); DUMPOUT << '\n'; return 0; } template int dump_out(const Head &head, const Tail &... tail) { dump_function(head); DUMPOUT << ' '; dump_out(tail...); return 0; } #ifdef DEBUG_ #define dump(x) \ DUMPOUT << #x << ": "; \ dump_function(x); \ DUMPOUT << endl; void dumps() {} template void dumps(const T &t) { dump_function(t); DUMPOUT << " "; } template void dumps(const Head &head, const Tail &... tail) { dump_function(head); DUMPOUT << ' '; dump_out(tail...); } #else #define dump(x) template void dumps(const T &...) {} #endif /* ----------------------- DEBUG FUNCTION ---------------------------- */ template struct Fp { long long val; constexpr Fp(long long v = 0) noexcept : val(v % MOD) { if (val < 0) val += MOD; } constexpr int getmod() const { return MOD; } constexpr Fp operator - () const noexcept { return val ? MOD - val : 0; } constexpr Fp operator + (const Fp& r) const noexcept { return Fp(*this) += r; } constexpr Fp operator - (const Fp& r) const noexcept { return Fp(*this) -= r; } constexpr Fp operator * (const Fp& r) const noexcept { return Fp(*this) *= r; } constexpr Fp operator / (const Fp& r) const noexcept { return Fp(*this) /= r; } constexpr Fp& operator += (const Fp& r) noexcept { val += r.val; if (val >= MOD) val -= MOD; return *this; } constexpr Fp& operator -= (const Fp& r) noexcept { val -= r.val; if (val < 0) val += MOD; return *this; } constexpr Fp& operator *= (const Fp& r) noexcept { val = val * r.val % MOD; return *this; } constexpr Fp& operator /= (const Fp& r) noexcept { long long a = r.val, b = MOD, u = 1, v = 0; while (b) { long long t = a / b; a -= t * b, swap(a, b); u -= t * v, swap(u, v); } val = val * u % MOD; if (val < 0) val += MOD; return *this; } constexpr bool operator == (const Fp& r) const noexcept { return this->val == r.val; } constexpr bool operator != (const Fp& r) const noexcept { return this->val != r.val; } friend constexpr istream& operator >> (istream& is, Fp& x) noexcept { is >> x.val; x.val %= MOD; if (x.val < 0) x.val += MOD; return is; } friend constexpr ostream& operator << (ostream& os, const Fp& x) noexcept { return os << x.val; } friend constexpr Fp modpow(const Fp& r, long long n) noexcept { if (n == 0) return 1; auto t = modpow(r, n / 2); t = t * t; if (n & 1) t = t * r; return t; } friend constexpr Fp modinv(const Fp& r) noexcept { long long a = r.val, b = MOD, u = 1, v = 0; while (b) { long long t = a / b; a -= t * b, swap(a, b); u -= t * v, swap(u, v); } return Fp(u); } }; template struct BiCoef { vector fact_, inv_, finv_; constexpr BiCoef() {} constexpr BiCoef(int n) noexcept : fact_(n, 1), inv_(n, 1), finv_(n, 1) { init(n); } constexpr void init(int n) noexcept { fact_.assign(n, 1), inv_.assign(n, 1), finv_.assign(n, 1); int MOD = fact_[0].getmod(); for(int i = 2; i < n; i++){ fact_[i] = fact_[i-1] * i; inv_[i] = -inv_[MOD%i] * (MOD/i); finv_[i] = finv_[i-1] * inv_[i]; } } constexpr T nCr(int n, int k) const noexcept { if (n < k || n < 0 || k < 0) return 0; int MOD = fact_[0].getmod(); if(n < MOD) return fact_[n] * finv_[k] * finv_[n-k]; // Lucasの定理 T ret = 1; while(n || k){ int _n = n % MOD, _k = k % MOD; n /= MOD; k /= MOD; ret *= nCr(_n, _k); } return ret; } constexpr T nPr(int n, int k) const noexcept { if (n < k || n < 0 || k < 0) return 0; return fact_[n] * finv_[n-k]; } constexpr T nHr(int n, int k) const noexcept { if (n < k || n < 0 || k < 0) return 0; return fact_[n] * finv_[n-k]; } constexpr T fact(int n) const noexcept { if (n < 0) return 0; return fact_[n]; } constexpr T inv(int n) const noexcept { if (n < 0) return 0; return inv_[n]; } constexpr T finv(int n) const noexcept { if (n < 0) return 0; return finv_[n]; } }; /* ----------------------------- MOD ----------------------------------- */ const int MOD = 1000000007; const int MOD2 = 998244353; using mint = Fp; BiCoef bc; // using vec = vector; // using mat = vector; /* ----------------------------- MOD ----------------------------------- */ /* ----------------------- AtCoder Library ---------------------------- */ // #include // using namespace atcoder; /* ----------------------- AtCoder Library ---------------------------- */ //UnionFind struct UnionFind { vector par; UnionFind(int n) : par(n, -1) { } void init(int n) { par.assign(n, -1); } int root(int x) { if (par[x] < 0) return x; else return par[x] = root(par[x]); } bool issame(int x, int y) { return root(x) == root(y); } bool unite(int x, int y) { x = root(x); y = root(y); if (x == y) return false; if (par[x] > par[y]) swap(x, y); // merge technique par[x] += par[y]; par[y] = x; return true; } int size(int x) { return -par[root(x)]; } }; class StronglyConnectedComponents{ int nodeNum; int size; public: vector> edge,revEdge; vector> graph; vector label,visited,order; // 帰りがけ順に番号を振っていく void dfs(int cur){ visited[cur] = 1; for(int next:edge[cur]) if(!visited[next]) dfs(next); order.push_back(cur); } // 通ることが出来た頂点に番号を振っていく void revDfs(int cur,int labelId){ visited[cur] = 1; label[cur] = labelId; for(int next:revEdge[cur]) if(!visited[next]) revDfs(next,labelId); } StronglyConnectedComponents(const int& nodeNum) : nodeNum(nodeNum), edge(nodeNum), revEdge(nodeNum), label(nodeNum), visited(nodeNum) {} void make_edge(const int from,const int to) { edge[from].push_back(to); revEdge[to].push_back(from); } void solve(void) { for(int i = 0; i < nodeNum; ++i) visited[i] = 0; for(int i = 0; i < nodeNum; ++i) if(!visited[i]) dfs(i); for(int i = 0; i < nodeNum; ++i) visited[i] = 0; reverse(order.begin(),order.end()); int labelId = 0; for(int i:order) if(!visited[i]) revDfs(i,labelId++); size = labelId; make_graph(); } // 強連結成分で縮約したグラフを作る void make_graph(){ graph.resize(size); for(int i = 0; i < edge.size(); i++){ for(auto to : edge[i]){ if(getLabel(i) == getLabel(to)) continue; graph[getLabel(i)].push_back(getLabel(to)); } } } vector> topological_sort(void){ vector> ret(size); for(int i = 0; i < nodeNum; i++) ret[label[i]].emplace_back(i); return ret; } void print(void) { for(auto labelId:label) cout << labelId << " "; cout << endl; } // vが属している強連結成分のラベル(トポロジカル順序) int getLabel(int v){ return label[v]; } int getSize(){ return size; } }; template struct Compress { vector v; Compress() {} Compress(vector _v) :v(_v) { build(); } void add(T x) { v.emplace_back(x); } void build() { sort(v.begin(), v.end()); v.erase(unique(v.begin(), v.end()), v.end()); } void build(vector _v) { v = _v; sort(v.begin(), v.end()); v.erase(unique(v.begin(), v.end()), v.end()); } int get(T x) { return lower_bound(v.begin(), v.end(), x) - v.begin(); } T& operator[](int i) { return v[i]; } int size() { return (int)v.size(); } }; void solve(){ int N, M; cin >> N >> M; UnionFind uf(N); vector U(M), V(M); REP(i,M){ cin >> U[i] >> V[i]; --U[i], --V[i]; uf.unite(U[i], V[i]); } vector>> v(N); REP(i,M){ int num = uf.root(U[i]); v[num].emplace_back(U[i], V[i]); } vector> ans; REP(i,N) if(!v[i].empty()){ Compress cmp; for(auto [u, v] : v[i]){ cmp.add(u); cmp.add(v); } cmp.build(); int sz = cmp.size(); StronglyConnectedComponents scc(sz); for(auto [u, v] : v[i]){ scc.make_edge(cmp.get(u), cmp.get(v)); } scc.solve(); if(scc.getSize() == sz){ REP(i,sz-1) ans.emplace_back(cmp[scc.getLabel(i)], cmp[scc.getLabel(i+1)]); } else{ REP(i,sz) ans.emplace_back(cmp[scc.getLabel(i)], cmp[scc.getLabel((i+1)%sz)]); } } cout << ans.size() << endl; for(auto [u, v] : ans) cout << u+1 << " " << v+1 << endl; } int main(){ cin.tie(0); ios::sync_with_stdio(false); solve(); // int T; cin >> T; REP(t,T) solve(); }