Problem:
Princess' Perfectionism
The princess has
n
spies, and she is planning to assign exactly
n
tasks to them.
Skills of the spies constrain the eligibilities for the tasks.
The princess is a perfectionist, and she is not satisfied unless
each spy is assigned exactly one task, and
each task is assigned to exactly one spy.
Fortunately, this turned out to be feasible;
there may be, however, selfish spies who cling to specific tasks among those eligible for themselves.
She plans to train some of the spies so that a satisfiable task
assignment is possible even when any single spy is selfish.
One session trains a spy so that he/she becomes eligible for a new task.
She wants to minimize the number of training sessions.
Your mission is to suggest an optimal training plan to the princess.
You are given all the eligible pairs of a spy and a task.
If no spy is selfish, the tasks can be assigned to the spies so that the princess is satisfied.

The eligible pairs should be augmented through training so that
an assignment that satisfies the princess always exists
even if any single spy clings to a task that is eligible for him/her.

Spies may cling to a task newly made eligible through training.
Note that no training at all might be required for the purpose.
Input
The input consists of multiple datasets, each in the following format.
n
m
s
1
t
1
...
s
m
t
m
n
is the number of spies as well as the number of tasks.
n
is a positive integer not exceeding 2000.
m
is the number of eligible pairs of a spy and a task.
m
is a positive integer not exceeding 10
5
.
The spies and tasks are respectively represented by integers between 1 and
n
.
For
i
= 1, 2, ...,
m,
(
s
i
, t
i
) is an eligible pair, which represents that the spy
s
i
is eligible for the task
t
i
.
In addition, if
i
≠
j,
then
s
i
≠
s
j
or
t
i
≠
t
j
.
It is guaranteed that there exists a subset of eligible pairs in which all spies and tasks appear exactly once, that is, the princess can be satisfied by some assignment if no spy is selfish.
The end of the input is indicated by a line containing two zeros with a space between them.
The number of datasets in the input is at most 25.
Output
For each dataset, output a nonnegative integer
k
in the first line, and in each of the next
k
lines, output two integers
x
i
and
y
i
(
i
= 1, 2, ...,
k
) with a space between them.
k
is the minimum number of pairs of a spy and a task
to be trained, and {(
x
1
,
y
1
), (
x
2
,
y
2
), ..., (
x
k
, y
k
)} is a set of pairs that attains the minimum, where
x
i
and
y
i
represent a spy and a task, respectively.
There may be two or more sets of pairs to be trained with the least number of elements; any of them shall be deemed correct.
Sample Input
2 3
1 1
1 2
2 2
2 2
1 1
2 2
4 7
1 1
1 2
2 2
3 2
3 3
3 4
4 4
5 10
1 1
1 2
1 3
2 1
2 2
2 4
3 3
4 4
4 5
5 5
0 0
Output for the Sample Input
1
2 1
0
2
2 3
4 1
2
3 2
5 3

Submitted solutions:

code_snippet: #line 2 "/home/nixos/workspace/CompPro-Make/library/KowerKoint/stl-expansion.hpp"
#include <bits/stdc++.h>

template <typename T1, typename T2>
std::istream& operator>>(std::istream& is, std::pair<T1, T2>& p) {
    is >> p.first >> p.second;
    return is;
}
template <typename T, size_t N>
std::istream& operator>>(std::istream& is, std::array<T, N>& a) {
    for (size_t i = 0; i < N; ++i) {
        is >> a[i];
    }
    return is;
}
template <typename T>
std::istream& operator>>(std::istream& is, std::vector<T>& v) {
    for (auto& e : v) is >> e;
    return is;
}
template <typename T1, typename T2>
std::ostream& operator<<(std::ostream& os, const std::pair<T1, T2>& p) {
    os << p.first << " " << p.second;
    return os;
}
template <typename T, size_t N>
std::ostream& operator<<(std::ostream& os, const std::array<T, N>& a) {
    for (size_t i = 0; i < N; ++i) {
        os << a[i] << (i + 1 == a.size() ? "" : " ");
    }
    return os;
}
template <typename T>
std::ostream& operator<<(std::ostream& os, const std::vector<T>& v) {
    for (size_t i = 0; i < v.size(); ++i) {
        os << v[i] << (i + 1 == v.size() ? "" : " ");
    }
    return os;
}
#line 3 "/home/nixos/workspace/CompPro-Make/library/KowerKoint/base.hpp"
using namespace std;

#define REP(i, n) for(int i = 0; i < (int)(n); i++)
#define FOR(i, a, b) for(ll i = a; i < (ll)(b); i++)
#define ALL(a) (a).begin(),(a).end()
#define RALL(a) (a).rbegin(),(a).rend()
#define END(...) { print(__VA_ARGS__); return; }

using VI = vector<int>;
using VVI = vector<VI>;
using VVVI = vector<VVI>;
using ll = long long;
using VL = vector<ll>;
using VVL = vector<VL>;
using VVVL = vector<VVL>;
using ull = unsigned long long;
using VUL = vector<ull>;
using VVUL = vector<VUL>;
using VVVUL = vector<VVUL>;
using VD = vector<double>;
using VVD = vector<VD>;
using VVVD = vector<VVD>;
using VS = vector<string>;
using VVS = vector<VS>;
using VVVS = vector<VVS>;
using VC = vector<char>;
using VVC = vector<VC>;
using VVVC = vector<VVC>;
using P = pair<int, int>;
using VP = vector<P>;
using VVP = vector<VP>;
using VVVP = vector<VVP>;
using LP = pair<ll, ll>;
using VLP = vector<LP>;
using VVLP = vector<VLP>;
using VVVLP = vector<VVLP>;

template <typename T>
using PQ = priority_queue<T>;
template <typename T>
using GPQ = priority_queue<T, vector<T>, greater<T>>;

constexpr int INF = 1001001001;
constexpr ll LINF = 1001001001001001001ll;
constexpr int DX[] = {1, 0, -1, 0};
constexpr int DY[] = {0, 1, 0, -1};

void print() { cout << '\n'; }
template<typename T>
void print(const T &t) { cout << t << '\n'; }
template<typename Head, typename... Tail>
void print(const Head &head, const Tail &... tail) {
    cout << head << ' ';
    print(tail...);
}

#ifdef DEBUG
void dbg() { cerr << '\n'; }
template<typename T>
void dbg(const T &t) { cerr << t << '\n'; }
template<typename Head, typename... Tail>
void dbg(const Head &head, const Tail &... tail) {
    cerr << head << ' ';
    dbg(tail...);
}
#else
template<typename... Args>
void dbg(const Args &... args) {}
#endif

template<typename T>
vector<vector<T>> split(typename vector<T>::const_iterator begin, typename vector<T>::const_iterator end, T val) {
    vector<vector<T>> res;
    vector<T> cur;
    for(auto it = begin; it != end; it++) {
        if(*it == val) {
            res.push_back(cur);
            cur.clear();
        } else cur.push_back(*it);
    }
    res.push_back(cur);
    return res;
}

vector<string> split(typename string::const_iterator begin, typename string::const_iterator end, char val) {
    vector<string> res;
    string cur = "";
    for(auto it = begin; it != end; it++) {
        if(*it == val) {
            res.push_back(cur);
            cur.clear();
        } else cur.push_back(*it);
    }
    res.push_back(cur);
    return res;
}

template< typename T1, typename T2 >
inline bool chmax(T1 &a, T2 b) { return a < b && (a = b, true); }

template< typename T1, typename T2 >
inline bool chmin(T1 &a, T2 b) { return a > b && (a = b, true); }

template <typename T>
pair<VI, vector<T>> compress(const vector<T> &a) {
    int n = a.size();
    vector<T> x;
    REP(i, n) x.push_back(a[i]);
    sort(ALL(x)); x.erase(unique(ALL(x)), x.end());
    VI res(n);
    REP(i, n) res[i] = lower_bound(ALL(x), a[i]) - x.begin();
    return make_pair(res, x);
}

template <typename It>
auto rle(It begin, It end) {
    vector<pair<typename It::value_type, int>> res;
    if(begin == end) return res;
    auto pre = *begin;
    int num = 1;
    for(auto it = begin + 1; it != end; it++) {
        if(pre != *it) {
            res.emplace_back(pre, num);
            pre = *it;
            num = 1;
        } else num++;
    }
    res.emplace_back(pre, num);
    return res;
}

template <typename It>
vector<pair<typename It::value_type, int>> rle_sort(It begin, It end) {
    vector<typename It::value_type> cloned(begin, end);
    sort(ALL(cloned));
    auto e = rle(ALL(cloned));
    sort(ALL(e), [](const auto& l, const auto& r) { return l.second < r.second; });
    return e;
}

template <typename T>
pair<vector<T>, vector<T>> factorial(int n) {
    vector<T> res(n+1), rev(n+1);
    res[0] = 1;
    REP(i, n) res[i+1] = res[i] * (i+1);
    rev[n] = 1 / res[n];
    for(int i = n; i > 0; i--) {
        rev[i-1] = rev[i] * i;
    }
    return make_pair(res, rev);
}
#line 2 "main.cpp"

template <typename T>
using V = vector<T>;
template <typename T>
using VV = vector<V<T>>;

/*
struct E {
  int to, rev, cap;
};
VV<E> g;
auto add_edge = [&](int from, int to, int cap) {
  g[from].push_back(E{to, int(g[to].size()), cap});
  g[to].push_back(E{from, int(g[from].size())-1, 0});
};
*/
template <class C>
struct MaxFlow {
  C flow;
  V<char> dual;  // false: S-side true: T-side
};
template <class C, class E>
struct MFExec {
  static constexpr C INF = numeric_limits<C>::max();
  C eps;
  VV<E>& g;
  int s, t;
  V<int> level, iter;
  C dfs(int v, C f) {
    if (v == t) return f;
    C res = 0;
    for (int& i = iter[v]; i < int(g[v].size()); i++) {
      E& e = g[v][i];
      if (e.cap <= eps || level[v] >= level[e.to]) continue;
      C d = dfs(e.to, min(f, e.cap));
      e.cap -= d;
      g[e.to][e.rev].cap += d;
      res += d;
      f -= d;
      if (f == 0) break;
    }
    return res;
  }
  MaxFlow<C> info;
  MFExec(VV<E>& _g, int _s, int _t, C _eps)
      : eps(_eps), g(_g), s(_s), t(_t) {
    int N = int(g.size());
    C& flow = (info.flow = 0);
    while (true) {
      queue<int> que;
      level = V<int>(N, -1);
      level[s] = 0;
      que.push(s);
      while (!que.empty()) {
        int v = que.front();
        que.pop();
        for (E e : g[v]) {
          if (e.cap <= eps || level[e.to] >= 0) continue;
          level[e.to] = level[v] + 1;
          que.push(e.to);
        }
      }
      if (level[t] == -1) break;
      while (true) {
        iter = V<int>(N, 0);
        C f = dfs(s, INF);
        if (!f) break;
        flow += f;
      }
    }
    for (int i = 0; i < N; i++)
      info.dual.push_back(level[i] == -1);
  }
};
template <class C, class E>
MaxFlow<C> get_mf(VV<E>& g, int s, int t, C eps) {
  return MFExec<C, E>(g, s, t, eps).info;
}

struct SCC {
  V<int> id;
  VV<int> groups;
};
template <class E>
struct SCCExec : SCC {
  int n;
  const VV<E>& g;
  int tm = 0;
  V<bool> flag;
  V<int> low, ord, st;
  void dfs(int v) {
    low[v] = ord[v] = tm++;
    st.push_back(v);
    flag[v] = true;
    for (auto e : g[v]) {
      if (ord[e.to] == -1) {
        dfs(e.to);
        low[v] = min(low[v], low[e.to]);
      } else if (flag[e.to]) {
        low[v] = min(low[v], ord[e.to]);
      }
    }
    if (low[v] == ord[v]) {
      V<int> gr;
      while (true) {
        int u = st.back();
        st.pop_back();
        gr.push_back(u);
        if (u == v) break;
      }
      for (int x : gr) flag[x] = false;
      groups.push_back(gr);
    }
  }
  SCCExec(const VV<E>& _g)
      : n(int(_g.size())), g(_g), flag(n), low(n), ord(n, -1) {
    id = V<int>(n);
    for (int i = 0; i < n; i++) {
      if (ord[i] == -1) dfs(i);
    }
    reverse(groups.begin(), groups.end());
    for (int i = 0; i < int(groups.size()); i++) {
      for (int x : groups[i]) {
        id[x] = i;
      }
    }
  }
};
template <class E>
SCC get_scc(const VV<E>& g) {
  return SCCExec<E>(g);
}

bool solve(){
    struct E {
      int to, rev, cap;
    };
    int n, m; cin >> n >> m;
    if(n == 0 && m == 0) return false;
    int src = n*2, sink = n*2+1;
    VV<E> g(n*2+2);
    auto add_edge = [&](int from, int to, int cap) {
      g[from].push_back(E{to, int(g[to].size()), cap});
      g[to].push_back(E{from, int(g[from].size())-1, 0});
    };
    REP(i, m) {
        int s, t; cin >> s >> t; s--; t--;
        add_edge(s, n+t, 1);
    }
    // tajuhen
    REP(i, n) {
        if(ssize(g[i]) == 1 && ssize(g[g[i][0].to]) == 1) {
            // cerr << "taju: " << i << ' ' << g[i][0].to << endl;
            add_edge(i, g[i][0].to, 1);
        }
    }
    REP(i, n) add_edge(src, i, 1);
    REP(i, n) add_edge(n+i, sink, 1);
    auto mf = get_mf(g, src, sink, 0);
    assert(mf.flow == n);
    struct ES { int to; };
    VV<ES> rg(n*2);
    for(int i = 0; i < n*2; i++) {
        for(const auto& e : g[i]) {
            // cerr << "rg:" << i << ' ' << e.to << ' ' << e.cap << endl;
            if(e.cap == 0) continue;
            if(e.to >= n*2) continue;
            rg[i].push_back({e.to});
        }
    }
    auto scc = get_scc(rg);
    for(auto& group : scc.groups) {
        ranges::sort(group);
    }
    int ng = ssize(scc.groups);
    vector<vector<int>> dag(ng);
    vector<int> in(ng), out(ng);
    for(int i = 0; i < n*2; i++) {
        for(const auto& e : rg[i]) {
            int u = scc.id[i], v = scc.id[e.to];
            if(u == v) continue;
            dag[u].push_back(v);
            out[u]++;
            in[v]++;
        }
    }
    vector<int> seen(ng);
    for(int i = 0; i < ng; i++) {
        if(in[i] == 0 && out[i] == 0) seen[i] = 1;
    }
    vector<int> enter, exit;
    for(int i = 0; i < ng; i++) {
        if(in[i]==0) enter.push_back(i);
        if(out[i] == 0) exit.push_back(i);
    }
    if(ssize(enter) == 0 && ssize(exit) == 0) {
        cout << "0\n";
        return true;
    }
    assert(ssize(enter) > 0 && ssize(exit) > 0);
    vector<int> st, ed;
    for(int i = 0; i < ssize(enter); i++) {
        if(seen[enter[i]]) continue;
        vector<int> path = {enter[i]};
        while(1) {
            bool proceed = false;
            int u = path.back();
            for(int v : dag[u]) {
                if(seen[v]) continue;
                path.push_back(v);
                proceed = true;
                break;
            }
            if(!proceed) break;
        }
        if(out[path.back()] == 0) {
            st.push_back(enter[i]);
            ed.push_back(path.back());
            for(int u : path) seen[u] = true;
        }
    }
    auto get_spy = [&](int u) {
        assert(scc.groups[u][0] < n);
        return scc.groups[u][0];
    };
    auto get_task = [&](int u) {
        assert(scc.groups[u].back() >= n);
        return scc.groups[u].back() - n;
    };
    vector<pair<int, int>> ans;
    for(int i = 0; i < ssize(st); i++) {
        int ni = i+1;
        if(ni == ssize(st)) ni = 0;
        ans.emplace_back(get_spy(ed[i]), get_task(st[ni]));
    }
    vector<int> enter_req, exit_req;
    for(int u : enter) {
        if(!seen[u]) {
            enter_req.push_back(u);
        }
    }
    for(int u : exit) {
        if(!seen[u]) {
            exit_req.push_back(u);
        }
    }
    int see = min(ssize(enter_req), ssize(exit_req));
    REP(i, see) {
        ans.emplace_back(get_spy(exit_req[i]), get_task(enter_req[i]));
    }
    for(int i = see; i < ssize(enter_req); i++) {
        ans.emplace_back(get_spy(ed[0]), get_task(enter_req[i]));
    }
    for(int i = see; i < ssize(exit_req); i++) {
        ans.emplace_back(get_spy(exit_req[i]), get_task(st[0]));
    }
    cout << ssize(ans) << '\n';
    for(auto [u, v] : ans) {
        cout << u+1 << ' ' << v+1 << '\n';
    }
    // for(const auto& group : scc.groups) {
    //     for(int i : group) {
    //         cerr << ' ' << i;
    //     }
    //     cerr << endl;
    // }
    // cerr << "st: ";
    // for(int u : st) {
    //     cerr << ' ' << u;
    // }
    // cerr << endl;
    // cerr << "ed: ";
    // for(int u : ed) {
    //     cerr << ' ' << u;
    // }
    // cerr << endl;
    return true;
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout << fixed << setprecision(10);
    while(solve());
}
accuracy: 1
time_ms: 230
memory_kb: 9472
final_rank: 6

code_snippet: #include <bits/stdc++.h>
using namespace std;
using ll = long long;
using ull = unsigned long long;
using uint = unsigned int;
#define rep(i, n) for (int i = 0; i < n; i++)
#define all(v) v.begin(), v.end()
template <class T, class U>
inline bool chmax(T& a, U b) {
    if (a < b) {
        a = b;
        return true;
    }
    return false;
}
template <class T, class U>
inline bool chmin(T& a, U b) {
    if (a > b) {
        a = b;
        return true;
    }
    return false;
}
template <class T>
inline void compress(vector<T>& a) {
    sort(a.begin(), a.end());
    a.erase(unique(a.begin(), a.end()), a.end());
}
template <class T>
T rand(T l, T r) {
    static mt19937 mt(random_device{}());
    // [l, r)
    if constexpr (is_integral_v<T>) {
        return uniform_int_distribution<T>(l, r - 1)(mt);
    } else if constexpr (is_floating_point_v<T>) {
        return uniform_real_distribution<T>(l, r)(mt);
    }
}
constexpr int INF = 1001001001;
constexpr ll llINF = 3000000000000000010;
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>
using namespace __gnu_pbds;
using pbds_set = tree<ll, null_type, less<ll>, rb_tree_tag, tree_order_statistics_node_update>;
using pbds_mset = tree<ll, null_type, less_equal<ll>, rb_tree_tag, tree_order_statistics_node_update>;
using pbds_umap = gp_hash_table<int, int>;
using pbds_trie = trie<string, null_type, trie_string_access_traits<>, pat_trie_tag, trie_prefix_search_node_update>;

#include <algorithm>
#include <cassert>
#include <vector>

namespace atcoder {

struct dsu {
   public:
    dsu() : _n(0) {}
    dsu(int n) : _n(n), parent_or_size(n, -1) {}

    int merge(int a, int b) {
        assert(0 <= a && a < _n);
        assert(0 <= b && b < _n);
        int x = leader(a), y = leader(b);
        if (x == y) return x;
        if (-parent_or_size[x] < -parent_or_size[y]) std::swap(x, y);
        parent_or_size[x] += parent_or_size[y];
        parent_or_size[y] = x;
        return x;
    }

    bool same(int a, int b) {
        assert(0 <= a && a < _n);
        assert(0 <= b && b < _n);
        return leader(a) == leader(b);
    }

    int leader(int a) {
        assert(0 <= a && a < _n);
        if (parent_or_size[a] < 0) return a;
        return parent_or_size[a] = leader(parent_or_size[a]);
    }

    int size(int a) {
        assert(0 <= a && a < _n);
        return -parent_or_size[leader(a)];
    }

    std::vector<std::vector<int>> groups() {
        std::vector<int> leader_buf(_n), group_size(_n);
        for (int i = 0; i < _n; i++) {
            leader_buf[i] = leader(i);
            group_size[leader_buf[i]]++;
        }
        std::vector<std::vector<int>> result(_n);
        for (int i = 0; i < _n; i++) {
            result[i].reserve(group_size[i]);
        }
        for (int i = 0; i < _n; i++) {
            result[leader_buf[i]].push_back(i);
        }
        result.erase(std::remove_if(result.begin(), result.end(), [&](const std::vector<int>& v) { return v.empty(); }), result.end());
        return result;
    }

   private:
    int _n;
    std::vector<int> parent_or_size;
};

}  // namespace atcoder

#include <algorithm>
#include <vector>

namespace atcoder {

namespace internal {

template <class T>
struct simple_queue {
    std::vector<T> payload;
    int pos = 0;
    void reserve(int n) { payload.reserve(n); }
    int size() const { return int(payload.size()) - pos; }
    bool empty() const { return pos == int(payload.size()); }
    void push(const T& t) { payload.push_back(t); }
    T& front() { return payload[pos]; }
    void clear() {
        payload.clear();
        pos = 0;
    }
    void pop() { pos++; }
};

}  // namespace internal

}  // namespace atcoder

#include <cassert>
#include <limits>
#include <queue>
#include <vector>

namespace atcoder {

template <class Cap>
struct mf_graph {
   public:
    mf_graph() : _n(0) {}
    mf_graph(int n) : _n(n), g(n) {}

    int add_edge(int from, int to, Cap cap) {
        assert(0 <= from && from < _n);
        assert(0 <= to && to < _n);
        assert(0 <= cap);
        int m = int(pos.size());
        pos.push_back({from, int(g[from].size())});
        int from_id = int(g[from].size());
        int to_id = int(g[to].size());
        if (from == to) to_id++;
        g[from].push_back(_edge{to, to_id, cap});
        g[to].push_back(_edge{from, from_id, 0});
        return m;
    }

    struct edge {
        int from, to;
        Cap cap, flow;
    };

    edge get_edge(int i) {
        int m = int(pos.size());
        assert(0 <= i && i < m);
        auto _e = g[pos[i].first][pos[i].second];
        auto _re = g[_e.to][_e.rev];
        return edge{pos[i].first, _e.to, _e.cap + _re.cap, _re.cap};
    }
    std::vector<edge> edges() {
        int m = int(pos.size());
        std::vector<edge> result;
        for (int i = 0; i < m; i++) {
            result.push_back(get_edge(i));
        }
        return result;
    }
    void change_edge(int i, Cap new_cap, Cap new_flow) {
        int m = int(pos.size());
        assert(0 <= i && i < m);
        assert(0 <= new_flow && new_flow <= new_cap);
        auto& _e = g[pos[i].first][pos[i].second];
        auto& _re = g[_e.to][_e.rev];
        _e.cap = new_cap - new_flow;
        _re.cap = new_flow;
    }

    Cap flow(int s, int t) { return flow(s, t, std::numeric_limits<Cap>::max()); }
    Cap flow(int s, int t, Cap flow_limit) {
        assert(0 <= s && s < _n);
        assert(0 <= t && t < _n);
        assert(s != t);

        std::vector<int> level(_n), iter(_n);
        internal::simple_queue<int> que;

        auto bfs = [&]() {
            std::fill(level.begin(), level.end(), -1);
            level[s] = 0;
            que.clear();
            que.push(s);
            while (!que.empty()) {
                int v = que.front();
                que.pop();
                for (auto e : g[v]) {
                    if (e.cap == 0 || level[e.to] >= 0) continue;
                    level[e.to] = level[v] + 1;
                    if (e.to == t) return;
                    que.push(e.to);
                }
            }
        };
        auto dfs = [&](auto self, int v, Cap up) {
            if (v == s) return up;
            Cap res = 0;
            int level_v = level[v];
            for (int& i = iter[v]; i < int(g[v].size()); i++) {
                _edge& e = g[v][i];
                if (level_v <= level[e.to] || g[e.to][e.rev].cap == 0) continue;
                Cap d = self(self, e.to, std::min(up - res, g[e.to][e.rev].cap));
                if (d <= 0) continue;
                g[v][i].cap += d;
                g[e.to][e.rev].cap -= d;
                res += d;
                if (res == up) break;
            }
            return res;
        };

        Cap flow = 0;
        while (flow < flow_limit) {
            bfs();
            if (level[t] == -1) break;
            std::fill(iter.begin(), iter.end(), 0);
            while (flow < flow_limit) {
                Cap f = dfs(dfs, t, flow_limit - flow);
                if (!f) break;
                flow += f;
            }
        }
        return flow;
    }

    std::vector<bool> min_cut(int s) {
        std::vector<bool> visited(_n);
        internal::simple_queue<int> que;
        que.push(s);
        while (!que.empty()) {
            int p = que.front();
            que.pop();
            visited[p] = true;
            for (auto e : g[p]) {
                if (e.cap && !visited[e.to]) {
                    visited[e.to] = true;
                    que.push(e.to);
                }
            }
        }
        return visited;
    }

   private:
    int _n;
    struct _edge {
        int to, rev;
        Cap cap;
    };
    std::vector<std::pair<int, int>> pos;
    std::vector<std::vector<_edge>> g;
};

}  // namespace atcoder

#include <algorithm>
#include <utility>
#include <vector>

namespace atcoder {
namespace internal {

template <class E>
struct csr {
    std::vector<int> start;
    std::vector<E> elist;
    csr(int n, const std::vector<std::pair<int, E>>& edges) : start(n + 1), elist(edges.size()) {
        for (auto e : edges) {
            start[e.first + 1]++;
        }
        for (int i = 1; i <= n; i++) {
            start[i] += start[i - 1];
        }
        auto counter = start;
        for (auto e : edges) {
            elist[counter[e.first]++] = e.second;
        }
    }
};

struct scc_graph {
   public:
    scc_graph(int n) : _n(n) {}

    int num_vertices() { return _n; }

    void add_edge(int from, int to) { edges.push_back({from, {to}}); }

    std::pair<int, std::vector<int>> scc_ids() {
        auto g = csr<edge>(_n, edges);
        int now_ord = 0, group_num = 0;
        std::vector<int> visited, low(_n), ord(_n, -1), ids(_n);
        visited.reserve(_n);
        auto dfs = [&](auto self, int v) -> void {
            low[v] = ord[v] = now_ord++;
            visited.push_back(v);
            for (int i = g.start[v]; i < g.start[v + 1]; i++) {
                auto to = g.elist[i].to;
                if (ord[to] == -1) {
                    self(self, to);
                    low[v] = std::min(low[v], low[to]);
                } else {
                    low[v] = std::min(low[v], ord[to]);
                }
            }
            if (low[v] == ord[v]) {
                while (true) {
                    int u = visited.back();
                    visited.pop_back();
                    ord[u] = _n;
                    ids[u] = group_num;
                    if (u == v) break;
                }
                group_num++;
            }
        };
        for (int i = 0; i < _n; i++) {
            if (ord[i] == -1) dfs(dfs, i);
        }
        for (auto& x : ids) {
            x = group_num - 1 - x;
        }
        return {group_num, ids};
    }

    std::vector<std::vector<int>> scc() {
        auto ids = scc_ids();
        int group_num = ids.first;
        std::vector<int> counts(group_num);
        for (auto x : ids.second) counts[x]++;
        std::vector<std::vector<int>> groups(ids.first);
        for (int i = 0; i < group_num; i++) {
            groups[i].reserve(counts[i]);
        }
        for (int i = 0; i < _n; i++) {
            groups[ids.second[i]].push_back(i);
        }
        return groups;
    }

   private:
    int _n;
    struct edge {
        int to;
    };
    std::vector<std::pair<int, edge>> edges;
};

}  // namespace internal

}  // namespace atcoder

#include <cassert>
#include <vector>

namespace atcoder {

struct scc_graph {
   public:
    scc_graph() : internal(0) {}
    scc_graph(int n) : internal(n) {}

    void add_edge(int from, int to) {
        int n = internal.num_vertices();
        assert(0 <= from && from < n);
        assert(0 <= to && to < n);
        internal.add_edge(from, to);
    }

    std::vector<std::vector<int>> scc() { return internal.scc(); }

   private:
    internal::scc_graph internal;
};

}  // namespace atcoder

void solve() {
    int n, m;
    cin >> n >> m;
    if (n == 0 && m == 0) exit(0);
    atcoder::mf_graph<int> g(2 * n + 2);
    vector<int> s(m), t(m);
    rep(i, m) {
        cin >> s[i] >> t[i];
        s[i]--, t[i]--;
        g.add_edge(s[i], t[i] + n, 1);
    }
    rep(i, n) {
        g.add_edge(2 * n, i, 1);
        g.add_edge(i + n, 2 * n + 1, 1);
    }
    assert(g.flow(2 * n, 2 * n + 1) == n);
    vector<int> p(n), ip(n);
    vector<pair<int, int>> rem;
    for (auto [from, to, cap, flow] : g.edges()) {
        if (from == 2 * n || to == 2 * n + 1) continue;
        to -= n;
        if (flow == 1) {
            p[from] = to;
            ip[to] = from;
        } else {
            rem.push_back({from, to});
        }
    }

    // 完全マッチングを0->0,1->1,...と置きなおす
    // 他の辺をu->vと書き直す
    // x->yが固定されたとき、y->xのパスがあればいい
    // 強連結であればよい
    atcoder::scc_graph sg(n);
    for (auto& [x, y] : rem) {
        // x->ip[y]
        y = ip[y];
        sg.add_edge(x, y);
    }
    auto scc = sg.scc();
    int k = scc.size();
    vector<int> group(n);
    rep(i, k) for (int x : scc[i]) group[x] = i;
    vector<int> in(k), out(k);
    atcoder::dsu uf(k);
    vector<vector<int>> dag(k);
    for (auto [x, y] : rem) {
        if (group[x] == group[y]) continue;
        dag[group[x]].push_back(group[y]);
        out[group[x]]++, in[group[y]]++;
        uf.merge(group[x], group[y]);
    }
    vector<vector<int>> src(k), snk(k);
    rep(i, k) {
        if (in[i] == 0 && out[i] == 0) continue;
        if (out[i] == 0) snk[uf.leader(i)].push_back(i);
        if (in[i] == 0) src[uf.leader(i)].push_back(i);
    }

    vector<pair<int, int>> ans;
    vector<bool> vis(k);
    vector<int> rA, rB;
    int S = -1, T = -1;
    vector<int> loopA, loopB;
    rep(i, k) {
        if (uf.leader(i) == i && uf.size(i) > 1) {
            auto A = src[i], B = snk[i];
            assert(!A.empty());
            S = A[0], T = B[0];
            function<bool(int)> dfs = [&](int x) {
                vis[x] = true;
                if (dag[x].empty()) {
                    loopB.push_back(x);
                    return true;
                }
                for (int y : dag[x]) {
                    if (vis[y]) continue;
                    if (dfs(y)) return true;
                }
                return false;
            };
            vector<bool> usedA(A.size()), usedB(B.size());
            rep(p, A.size()) {
                if (dfs(A[p])) {
                    loopA.push_back(A[p]);
                    usedA[p] = true;
                }
            }
            rep(p, B.size()) {
                if (vis[B[p]]) usedB[p] = true;
            }
            rep(p, A.size()) {
                if (!usedA[p]) rA.push_back(A[p]);
            }
            rep(p, B.size()) {
                if (!usedB[p]) rB.push_back(B[p]);
            }
        }
    }

    if (loopA.size()) {
        int l = loopA.size();
        rep(j, l) ans.push_back({loopB[j], loopA[(j + 1) % l]});
    }
    if (rA.size() || rB.size()) {
        if (rA.empty()) rA.push_back(S);
        if (rB.empty()) rB.push_back(T);
        while (rA.size() < rB.size()) rA.push_back(rA.back());
        while (rB.size() < rA.size()) rB.push_back(rB.back());
        rep(j, rA.size()) { ans.push_back({rB[j], rA[j]}); }
    }
    cout << ans.size() << endl;
    for (auto [x, y] : ans) {
        cout << scc[x][0] + 1 << " " << p[scc[y][0]] + 1 << endl;
    }
}
int main() {
    cin.tie(0);
    ios::sync_with_stdio(false);
    int t = 1;
    // cin >> t;
    while (1) solve();
}
accuracy: 1
time_ms: 160
memory_kb: 13532
final_rank: 7

code_snippet: #include <bits/stdc++.h>
using namespace std;
using ll = long long;
using ull = unsigned long long;
using uint = unsigned int;
#define rep(i, n) for (int i = 0; i < n; i++)
#define all(v) v.begin(), v.end()
template <class T, class U>
inline bool chmax(T& a, U b) {
    if (a < b) {
        a = b;
        return true;
    }
    return false;
}
template <class T, class U>
inline bool chmin(T& a, U b) {
    if (a > b) {
        a = b;
        return true;
    }
    return false;
}
template <class T>
inline void compress(vector<T>& a) {
    sort(a.begin(), a.end());
    a.erase(unique(a.begin(), a.end()), a.end());
}
template <class T>
T rand(T l, T r) {
    static mt19937 mt(random_device{}());
    // [l, r)
    if constexpr (is_integral_v<T>) {
        return uniform_int_distribution<T>(l, r - 1)(mt);
    } else if constexpr (is_floating_point_v<T>) {
        return uniform_real_distribution<T>(l, r)(mt);
    }
}
constexpr int INF = 1001001001;
constexpr ll llINF = 3000000000000000010;
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>
using namespace __gnu_pbds;
using pbds_set = tree<ll, null_type, less<ll>, rb_tree_tag, tree_order_statistics_node_update>;
using pbds_mset = tree<ll, null_type, less_equal<ll>, rb_tree_tag, tree_order_statistics_node_update>;
using pbds_umap = gp_hash_table<int, int>;
using pbds_trie = trie<string, null_type, trie_string_access_traits<>, pat_trie_tag, trie_prefix_search_node_update>;

#include <algorithm>
#include <cassert>
#include <vector>

namespace atcoder {

struct dsu {
   public:
    dsu() : _n(0) {}
    dsu(int n) : _n(n), parent_or_size(n, -1) {}

    int merge(int a, int b) {
        assert(0 <= a && a < _n);
        assert(0 <= b && b < _n);
        int x = leader(a), y = leader(b);
        if (x == y) return x;
        if (-parent_or_size[x] < -parent_or_size[y]) std::swap(x, y);
        parent_or_size[x] += parent_or_size[y];
        parent_or_size[y] = x;
        return x;
    }

    bool same(int a, int b) {
        assert(0 <= a && a < _n);
        assert(0 <= b && b < _n);
        return leader(a) == leader(b);
    }

    int leader(int a) {
        assert(0 <= a && a < _n);
        if (parent_or_size[a] < 0) return a;
        return parent_or_size[a] = leader(parent_or_size[a]);
    }

    int size(int a) {
        assert(0 <= a && a < _n);
        return -parent_or_size[leader(a)];
    }

    std::vector<std::vector<int>> groups() {
        std::vector<int> leader_buf(_n), group_size(_n);
        for (int i = 0; i < _n; i++) {
            leader_buf[i] = leader(i);
            group_size[leader_buf[i]]++;
        }
        std::vector<std::vector<int>> result(_n);
        for (int i = 0; i < _n; i++) {
            result[i].reserve(group_size[i]);
        }
        for (int i = 0; i < _n; i++) {
            result[leader_buf[i]].push_back(i);
        }
        result.erase(std::remove_if(result.begin(), result.end(), [&](const std::vector<int>& v) { return v.empty(); }), result.end());
        return result;
    }

   private:
    int _n;
    std::vector<int> parent_or_size;
};

}  // namespace atcoder

#include <algorithm>
#include <vector>

namespace atcoder {

namespace internal {

template <class T>
struct simple_queue {
    std::vector<T> payload;
    int pos = 0;
    void reserve(int n) { payload.reserve(n); }
    int size() const { return int(payload.size()) - pos; }
    bool empty() const { return pos == int(payload.size()); }
    void push(const T& t) { payload.push_back(t); }
    T& front() { return payload[pos]; }
    void clear() {
        payload.clear();
        pos = 0;
    }
    void pop() { pos++; }
};

}  // namespace internal

}  // namespace atcoder

#include <cassert>
#include <limits>
#include <queue>
#include <vector>

namespace atcoder {

template <class Cap>
struct mf_graph {
   public:
    mf_graph() : _n(0) {}
    mf_graph(int n) : _n(n), g(n) {}

    int add_edge(int from, int to, Cap cap) {
        assert(0 <= from && from < _n);
        assert(0 <= to && to < _n);
        assert(0 <= cap);
        int m = int(pos.size());
        pos.push_back({from, int(g[from].size())});
        int from_id = int(g[from].size());
        int to_id = int(g[to].size());
        if (from == to) to_id++;
        g[from].push_back(_edge{to, to_id, cap});
        g[to].push_back(_edge{from, from_id, 0});
        return m;
    }

    struct edge {
        int from, to;
        Cap cap, flow;
    };

    edge get_edge(int i) {
        int m = int(pos.size());
        assert(0 <= i && i < m);
        auto _e = g[pos[i].first][pos[i].second];
        auto _re = g[_e.to][_e.rev];
        return edge{pos[i].first, _e.to, _e.cap + _re.cap, _re.cap};
    }
    std::vector<edge> edges() {
        int m = int(pos.size());
        std::vector<edge> result;
        for (int i = 0; i < m; i++) {
            result.push_back(get_edge(i));
        }
        return result;
    }
    void change_edge(int i, Cap new_cap, Cap new_flow) {
        int m = int(pos.size());
        assert(0 <= i && i < m);
        assert(0 <= new_flow && new_flow <= new_cap);
        auto& _e = g[pos[i].first][pos[i].second];
        auto& _re = g[_e.to][_e.rev];
        _e.cap = new_cap - new_flow;
        _re.cap = new_flow;
    }

    Cap flow(int s, int t) { return flow(s, t, std::numeric_limits<Cap>::max()); }
    Cap flow(int s, int t, Cap flow_limit) {
        assert(0 <= s && s < _n);
        assert(0 <= t && t < _n);
        assert(s != t);

        std::vector<int> level(_n), iter(_n);
        internal::simple_queue<int> que;

        auto bfs = [&]() {
            std::fill(level.begin(), level.end(), -1);
            level[s] = 0;
            que.clear();
            que.push(s);
            while (!que.empty()) {
                int v = que.front();
                que.pop();
                for (auto e : g[v]) {
                    if (e.cap == 0 || level[e.to] >= 0) continue;
                    level[e.to] = level[v] + 1;
                    if (e.to == t) return;
                    que.push(e.to);
                }
            }
        };
        auto dfs = [&](auto self, int v, Cap up) {
            if (v == s) return up;
            Cap res = 0;
            int level_v = level[v];
            for (int& i = iter[v]; i < int(g[v].size()); i++) {
                _edge& e = g[v][i];
                if (level_v <= level[e.to] || g[e.to][e.rev].cap == 0) continue;
                Cap d = self(self, e.to, std::min(up - res, g[e.to][e.rev].cap));
                if (d <= 0) continue;
                g[v][i].cap += d;
                g[e.to][e.rev].cap -= d;
                res += d;
                if (res == up) break;
            }
            return res;
        };

        Cap flow = 0;
        while (flow < flow_limit) {
            bfs();
            if (level[t] == -1) break;
            std::fill(iter.begin(), iter.end(), 0);
            while (flow < flow_limit) {
                Cap f = dfs(dfs, t, flow_limit - flow);
                if (!f) break;
                flow += f;
            }
        }
        return flow;
    }

    std::vector<bool> min_cut(int s) {
        std::vector<bool> visited(_n);
        internal::simple_queue<int> que;
        que.push(s);
        while (!que.empty()) {
            int p = que.front();
            que.pop();
            visited[p] = true;
            for (auto e : g[p]) {
                if (e.cap && !visited[e.to]) {
                    visited[e.to] = true;
                    que.push(e.to);
                }
            }
        }
        return visited;
    }

   private:
    int _n;
    struct _edge {
        int to, rev;
        Cap cap;
    };
    std::vector<std::pair<int, int>> pos;
    std::vector<std::vector<_edge>> g;
};

}  // namespace atcoder

#include <algorithm>
#include <utility>
#include <vector>

namespace atcoder {
namespace internal {

template <class E>
struct csr {
    std::vector<int> start;
    std::vector<E> elist;
    csr(int n, const std::vector<std::pair<int, E>>& edges) : start(n + 1), elist(edges.size()) {
        for (auto e : edges) {
            start[e.first + 1]++;
        }
        for (int i = 1; i <= n; i++) {
            start[i] += start[i - 1];
        }
        auto counter = start;
        for (auto e : edges) {
            elist[counter[e.first]++] = e.second;
        }
    }
};

struct scc_graph {
   public:
    scc_graph(int n) : _n(n) {}

    int num_vertices() { return _n; }

    void add_edge(int from, int to) { edges.push_back({from, {to}}); }

    std::pair<int, std::vector<int>> scc_ids() {
        auto g = csr<edge>(_n, edges);
        int now_ord = 0, group_num = 0;
        std::vector<int> visited, low(_n), ord(_n, -1), ids(_n);
        visited.reserve(_n);
        auto dfs = [&](auto self, int v) -> void {
            low[v] = ord[v] = now_ord++;
            visited.push_back(v);
            for (int i = g.start[v]; i < g.start[v + 1]; i++) {
                auto to = g.elist[i].to;
                if (ord[to] == -1) {
                    self(self, to);
                    low[v] = std::min(low[v], low[to]);
                } else {
                    low[v] = std::min(low[v], ord[to]);
                }
            }
            if (low[v] == ord[v]) {
                while (true) {
                    int u = visited.back();
                    visited.pop_back();
                    ord[u] = _n;
                    ids[u] = group_num;
                    if (u == v) break;
                }
                group_num++;
            }
        };
        for (int i = 0; i < _n; i++) {
            if (ord[i] == -1) dfs(dfs, i);
        }
        for (auto& x : ids) {
            x = group_num - 1 - x;
        }
        return {group_num, ids};
    }

    std::vector<std::vector<int>> scc() {
        auto ids = scc_ids();
        int group_num = ids.first;
        std::vector<int> counts(group_num);
        for (auto x : ids.second) counts[x]++;
        std::vector<std::vector<int>> groups(ids.first);
        for (int i = 0; i < group_num; i++) {
            groups[i].reserve(counts[i]);
        }
        for (int i = 0; i < _n; i++) {
            groups[ids.second[i]].push_back(i);
        }
        return groups;
    }

   private:
    int _n;
    struct edge {
        int to;
    };
    std::vector<std::pair<int, edge>> edges;
};

}  // namespace internal

}  // namespace atcoder

#include <cassert>
#include <vector>

namespace atcoder {

struct scc_graph {
   public:
    scc_graph() : internal(0) {}
    scc_graph(int n) : internal(n) {}

    void add_edge(int from, int to) {
        int n = internal.num_vertices();
        assert(0 <= from && from < n);
        assert(0 <= to && to < n);
        internal.add_edge(from, to);
    }

    std::vector<std::vector<int>> scc() { return internal.scc(); }

   private:
    internal::scc_graph internal;
};

}  // namespace atcoder

void solve() {
    int n, m;
    cin >> n >> m;
    if (n == 0 && m == 0) exit(0);
    atcoder::mf_graph<int> g(2 * n + 2);
    vector<int> s(m), t(m);
    rep(i, m) {
        cin >> s[i] >> t[i];
        s[i]--, t[i]--;
        g.add_edge(s[i], t[i] + n, 1);
    }
    rep(i, n) {
        g.add_edge(2 * n, i, 1);
        g.add_edge(i + n, 2 * n + 1, 1);
    }
    assert(g.flow(2 * n, 2 * n + 1) == n);
    vector<int> p(n), ip(n);
    vector<pair<int, int>> rem;
    for (auto [from, to, cap, flow] : g.edges()) {
        if (from == 2 * n || to == 2 * n + 1) continue;
        to -= n;
        if (flow == 1) {
            p[from] = to;
            ip[to] = from;
        } else {
            rem.push_back({from, to});
        }
    }

    // 完全マッチングを0->0,1->1,...と置きなおす
    // 他の辺をu->vと書き直す
    // x->yが固定されたとき、y->xのパスがあればいい
    // 強連結であればよい
    atcoder::scc_graph sg(n);
    for (auto& [x, y] : rem) {
        // x->ip[y]
        y = ip[y];
        sg.add_edge(x, y);
    }
    auto scc = sg.scc();
    int k = scc.size();
    vector<int> group(n);
    rep(i, k) for (int x : scc[i]) group[x] = i;
    vector<int> in(k), out(k);
    atcoder::dsu uf(k);
    vector<vector<int>> dag(k);
    for (auto [x, y] : rem) {
        if (group[x] == group[y]) continue;
        dag[group[x]].push_back(group[y]);
        out[group[x]]++, in[group[y]]++;
        uf.merge(group[x], group[y]);
    }
    vector<vector<int>> src(k), snk(k);
    rep(i, k) {
        if (in[i] == 0 && out[i] == 0) continue;
        if (out[i] == 0) snk[uf.leader(i)].push_back(i);
        if (in[i] == 0) src[uf.leader(i)].push_back(i);
    }

    vector<pair<int, int>> ans;
    vector<bool> vis(k);
    vector<int> rA, rB;
    int S = -1, T = -1;
    rep(i, k) {
        if (uf.leader(i) == i && uf.size(i) > 1) {
            auto A = src[i], B = snk[i];
            assert(!A.empty());
            S = A[0], T = B[0];
            vector<int> loopA, loopB;
            function<bool(int)> dfs = [&](int x) {
                vis[x] = true;
                if (dag[x].empty()) {
                    loopB.push_back(x);
                    return true;
                }
                for (int y : dag[x]) {
                    if (vis[y]) continue;
                    if (dfs(y)) return true;
                }
                return false;
            };
            vector<bool> usedA(A.size()), usedB(B.size());
            rep(p, A.size()) {
                if (dfs(A[p])) {
                    loopA.push_back(A[p]);
                    usedA[p] = true;
                }
            }
            rep(p, B.size()) {
                if (vis[B[p]]) usedB[p] = true;
            }
            if (loopA.size()) {
                int l = loopA.size();
                rep(j, l) ans.push_back({loopB[j], loopA[(j + 1) % l]});
            }
            rep(p, A.size()) {
                if (!usedA[p]) rA.push_back(A[p]);
            }
            rep(p, B.size()) {
                if (!usedB[p]) rB.push_back(B[p]);
            }
        }
    }

    if (rA.size() || rB.size()) {
        if (rA.empty()) rA.push_back(S);
        if (rB.empty()) rB.push_back(T);
        while (rA.size() < rB.size()) rA.push_back(rA.back());
        while (rB.size() < rA.size()) rB.push_back(rB.back());
        rep(j, rA.size()) { ans.push_back({rB[j], rA[j]}); }
    }
    cout << ans.size() << endl;
    for (auto [x, y] : ans) {
        cout << scc[x][0] + 1 << " " << p[scc[y][0]] + 1 << endl;
    }
}
int main() {
    cin.tie(0);
    ios::sync_with_stdio(false);
    int t = 1;
    // cin >> t;
    while (1) solve();
}
accuracy: 0.5
time_ms: 170
memory_kb: 13324
final_rank: 13

code_snippet: #include <bits/stdc++.h>
using namespace std;
using ll = long long;
using ull = unsigned long long;
using uint = unsigned int;
#define rep(i, n) for (int i = 0; i < n; i++)
#define all(v) v.begin(), v.end()
template <class T, class U>
inline bool chmax(T& a, U b) {
    if (a < b) {
        a = b;
        return true;
    }
    return false;
}
template <class T, class U>
inline bool chmin(T& a, U b) {
    if (a > b) {
        a = b;
        return true;
    }
    return false;
}
template <class T>
inline void compress(vector<T>& a) {
    sort(a.begin(), a.end());
    a.erase(unique(a.begin(), a.end()), a.end());
}
template <class T>
T rand(T l, T r) {
    static mt19937 mt(random_device{}());
    // [l, r)
    if constexpr (is_integral_v<T>) {
        return uniform_int_distribution<T>(l, r - 1)(mt);
    } else if constexpr (is_floating_point_v<T>) {
        return uniform_real_distribution<T>(l, r)(mt);
    }
}
constexpr int INF = 1001001001;
constexpr ll llINF = 3000000000000000010;
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>
using namespace __gnu_pbds;
using pbds_set = tree<ll, null_type, less<ll>, rb_tree_tag, tree_order_statistics_node_update>;
using pbds_mset = tree<ll, null_type, less_equal<ll>, rb_tree_tag, tree_order_statistics_node_update>;
using pbds_umap = gp_hash_table<int, int>;
using pbds_trie = trie<string, null_type, trie_string_access_traits<>, pat_trie_tag, trie_prefix_search_node_update>;

#include <algorithm>
#include <cassert>
#include <vector>

namespace atcoder {

struct dsu {
   public:
    dsu() : _n(0) {}
    dsu(int n) : _n(n), parent_or_size(n, -1) {}

    int merge(int a, int b) {
        assert(0 <= a && a < _n);
        assert(0 <= b && b < _n);
        int x = leader(a), y = leader(b);
        if (x == y) return x;
        if (-parent_or_size[x] < -parent_or_size[y]) std::swap(x, y);
        parent_or_size[x] += parent_or_size[y];
        parent_or_size[y] = x;
        return x;
    }

    bool same(int a, int b) {
        assert(0 <= a && a < _n);
        assert(0 <= b && b < _n);
        return leader(a) == leader(b);
    }

    int leader(int a) {
        assert(0 <= a && a < _n);
        if (parent_or_size[a] < 0) return a;
        return parent_or_size[a] = leader(parent_or_size[a]);
    }

    int size(int a) {
        assert(0 <= a && a < _n);
        return -parent_or_size[leader(a)];
    }

    std::vector<std::vector<int>> groups() {
        std::vector<int> leader_buf(_n), group_size(_n);
        for (int i = 0; i < _n; i++) {
            leader_buf[i] = leader(i);
            group_size[leader_buf[i]]++;
        }
        std::vector<std::vector<int>> result(_n);
        for (int i = 0; i < _n; i++) {
            result[i].reserve(group_size[i]);
        }
        for (int i = 0; i < _n; i++) {
            result[leader_buf[i]].push_back(i);
        }
        result.erase(std::remove_if(result.begin(), result.end(), [&](const std::vector<int>& v) { return v.empty(); }), result.end());
        return result;
    }

   private:
    int _n;
    std::vector<int> parent_or_size;
};

}  // namespace atcoder

#include <algorithm>
#include <vector>

namespace atcoder {

namespace internal {

template <class T>
struct simple_queue {
    std::vector<T> payload;
    int pos = 0;
    void reserve(int n) { payload.reserve(n); }
    int size() const { return int(payload.size()) - pos; }
    bool empty() const { return pos == int(payload.size()); }
    void push(const T& t) { payload.push_back(t); }
    T& front() { return payload[pos]; }
    void clear() {
        payload.clear();
        pos = 0;
    }
    void pop() { pos++; }
};

}  // namespace internal

}  // namespace atcoder

#include <cassert>
#include <limits>
#include <queue>
#include <vector>

namespace atcoder {

template <class Cap>
struct mf_graph {
   public:
    mf_graph() : _n(0) {}
    mf_graph(int n) : _n(n), g(n) {}

    int add_edge(int from, int to, Cap cap) {
        assert(0 <= from && from < _n);
        assert(0 <= to && to < _n);
        assert(0 <= cap);
        int m = int(pos.size());
        pos.push_back({from, int(g[from].size())});
        int from_id = int(g[from].size());
        int to_id = int(g[to].size());
        if (from == to) to_id++;
        g[from].push_back(_edge{to, to_id, cap});
        g[to].push_back(_edge{from, from_id, 0});
        return m;
    }

    struct edge {
        int from, to;
        Cap cap, flow;
    };

    edge get_edge(int i) {
        int m = int(pos.size());
        assert(0 <= i && i < m);
        auto _e = g[pos[i].first][pos[i].second];
        auto _re = g[_e.to][_e.rev];
        return edge{pos[i].first, _e.to, _e.cap + _re.cap, _re.cap};
    }
    std::vector<edge> edges() {
        int m = int(pos.size());
        std::vector<edge> result;
        for (int i = 0; i < m; i++) {
            result.push_back(get_edge(i));
        }
        return result;
    }
    void change_edge(int i, Cap new_cap, Cap new_flow) {
        int m = int(pos.size());
        assert(0 <= i && i < m);
        assert(0 <= new_flow && new_flow <= new_cap);
        auto& _e = g[pos[i].first][pos[i].second];
        auto& _re = g[_e.to][_e.rev];
        _e.cap = new_cap - new_flow;
        _re.cap = new_flow;
    }

    Cap flow(int s, int t) { return flow(s, t, std::numeric_limits<Cap>::max()); }
    Cap flow(int s, int t, Cap flow_limit) {
        assert(0 <= s && s < _n);
        assert(0 <= t && t < _n);
        assert(s != t);

        std::vector<int> level(_n), iter(_n);
        internal::simple_queue<int> que;

        auto bfs = [&]() {
            std::fill(level.begin(), level.end(), -1);
            level[s] = 0;
            que.clear();
            que.push(s);
            while (!que.empty()) {
                int v = que.front();
                que.pop();
                for (auto e : g[v]) {
                    if (e.cap == 0 || level[e.to] >= 0) continue;
                    level[e.to] = level[v] + 1;
                    if (e.to == t) return;
                    que.push(e.to);
                }
            }
        };
        auto dfs = [&](auto self, int v, Cap up) {
            if (v == s) return up;
            Cap res = 0;
            int level_v = level[v];
            for (int& i = iter[v]; i < int(g[v].size()); i++) {
                _edge& e = g[v][i];
                if (level_v <= level[e.to] || g[e.to][e.rev].cap == 0) continue;
                Cap d = self(self, e.to, std::min(up - res, g[e.to][e.rev].cap));
                if (d <= 0) continue;
                g[v][i].cap += d;
                g[e.to][e.rev].cap -= d;
                res += d;
                if (res == up) break;
            }
            return res;
        };

        Cap flow = 0;
        while (flow < flow_limit) {
            bfs();
            if (level[t] == -1) break;
            std::fill(iter.begin(), iter.end(), 0);
            while (flow < flow_limit) {
                Cap f = dfs(dfs, t, flow_limit - flow);
                if (!f) break;
                flow += f;
            }
        }
        return flow;
    }

    std::vector<bool> min_cut(int s) {
        std::vector<bool> visited(_n);
        internal::simple_queue<int> que;
        que.push(s);
        while (!que.empty()) {
            int p = que.front();
            que.pop();
            visited[p] = true;
            for (auto e : g[p]) {
                if (e.cap && !visited[e.to]) {
                    visited[e.to] = true;
                    que.push(e.to);
                }
            }
        }
        return visited;
    }

   private:
    int _n;
    struct _edge {
        int to, rev;
        Cap cap;
    };
    std::vector<std::pair<int, int>> pos;
    std::vector<std::vector<_edge>> g;
};

}  // namespace atcoder

#include <algorithm>
#include <utility>
#include <vector>

namespace atcoder {
namespace internal {

template <class E>
struct csr {
    std::vector<int> start;
    std::vector<E> elist;
    csr(int n, const std::vector<std::pair<int, E>>& edges) : start(n + 1), elist(edges.size()) {
        for (auto e : edges) {
            start[e.first + 1]++;
        }
        for (int i = 1; i <= n; i++) {
            start[i] += start[i - 1];
        }
        auto counter = start;
        for (auto e : edges) {
            elist[counter[e.first]++] = e.second;
        }
    }
};

struct scc_graph {
   public:
    scc_graph(int n) : _n(n) {}

    int num_vertices() { return _n; }

    void add_edge(int from, int to) { edges.push_back({from, {to}}); }

    std::pair<int, std::vector<int>> scc_ids() {
        auto g = csr<edge>(_n, edges);
        int now_ord = 0, group_num = 0;
        std::vector<int> visited, low(_n), ord(_n, -1), ids(_n);
        visited.reserve(_n);
        auto dfs = [&](auto self, int v) -> void {
            low[v] = ord[v] = now_ord++;
            visited.push_back(v);
            for (int i = g.start[v]; i < g.start[v + 1]; i++) {
                auto to = g.elist[i].to;
                if (ord[to] == -1) {
                    self(self, to);
                    low[v] = std::min(low[v], low[to]);
                } else {
                    low[v] = std::min(low[v], ord[to]);
                }
            }
            if (low[v] == ord[v]) {
                while (true) {
                    int u = visited.back();
                    visited.pop_back();
                    ord[u] = _n;
                    ids[u] = group_num;
                    if (u == v) break;
                }
                group_num++;
            }
        };
        for (int i = 0; i < _n; i++) {
            if (ord[i] == -1) dfs(dfs, i);
        }
        for (auto& x : ids) {
            x = group_num - 1 - x;
        }
        return {group_num, ids};
    }

    std::vector<std::vector<int>> scc() {
        auto ids = scc_ids();
        int group_num = ids.first;
        std::vector<int> counts(group_num);
        for (auto x : ids.second) counts[x]++;
        std::vector<std::vector<int>> groups(ids.first);
        for (int i = 0; i < group_num; i++) {
            groups[i].reserve(counts[i]);
        }
        for (int i = 0; i < _n; i++) {
            groups[ids.second[i]].push_back(i);
        }
        return groups;
    }

   private:
    int _n;
    struct edge {
        int to;
    };
    std::vector<std::pair<int, edge>> edges;
};

}  // namespace internal

}  // namespace atcoder

#include <cassert>
#include <vector>

namespace atcoder {

struct scc_graph {
   public:
    scc_graph() : internal(0) {}
    scc_graph(int n) : internal(n) {}

    void add_edge(int from, int to) {
        int n = internal.num_vertices();
        assert(0 <= from && from < n);
        assert(0 <= to && to < n);
        internal.add_edge(from, to);
    }

    std::vector<std::vector<int>> scc() { return internal.scc(); }

   private:
    internal::scc_graph internal;
};

}  // namespace atcoder

void solve() {
    int n, m;
    cin >> n >> m;
    if (n == 0 && m == 0) exit(0);
    atcoder::mf_graph<int> g(2 * n + 2);
    vector<int> s(m), t(m);
    rep(i, m) {
        cin >> s[i] >> t[i];
        s[i]--, t[i]--;
        g.add_edge(s[i], t[i] + n, 1);
    }
    rep(i, n) {
        g.add_edge(2 * n, i, 1);
        g.add_edge(i + n, 2 * n + 1, 1);
    }
    assert(g.flow(2 * n, 2 * n + 1) == n);
    vector<int> p(n), ip(n);
    vector<pair<int, int>> rem;
    for (auto [from, to, cap, flow] : g.edges()) {
        if (from == 2 * n || to == 2 * n + 1) continue;
        to -= n;
        if (flow == 1) {
            p[from] = to;
            ip[to] = from;
        } else {
            rem.push_back({from, to});
        }
    }

    // 完全マッチングを0->0,1->1,...と置きなおす
    // 他の辺をu->vと書き直す
    // x->yが固定されたとき、y->xのパスがあればいい
    // 強連結であればよい
    atcoder::scc_graph sg(n);
    for (auto& [x, y] : rem) {
        // x->ip[y]
        y = ip[y];
        sg.add_edge(x, y);
    }
    auto scc = sg.scc();
    int k = scc.size();
    vector<int> group(n);
    rep(i, k) for (int x : scc[i]) group[x] = i;
    vector<int> in(k), out(k);
    atcoder::dsu uf(k);
    vector<vector<int>> dag(k);
    for (auto [x, y] : rem) {
        if (group[x] == group[y]) continue;
        dag[group[x]].push_back(group[y]);
        out[group[x]]++, in[group[y]]++;
        uf.merge(group[x], group[y]);
    }
    vector<vector<int>> src(k), snk(k);
    rep(i, k) {
        if (in[i] == 0 && out[i] == 0) continue;
        if (out[i] == 0) snk[uf.leader(i)].push_back(i);
        if (in[i] == 0) src[uf.leader(i)].push_back(i);
    }

    vector<pair<int, int>> ans;
    vector<bool> vis(k);
    rep(i, k) {
        if (uf.leader(i) == i && uf.size(i) > 1) {
            auto A = src[i], B = snk[i];
            assert(!A.empty());
            vector<int> loopA, loopB;
            function<bool(int)> dfs = [&](int x) {
                vis[x] = true;
                if (dag[x].empty()) {
                    loopB.push_back(x);
                    return true;
                }
                for (int y : dag[x]) {
                    if (vis[y]) continue;
                    if (dfs(y)) return true;
                }
                return false;
            };
            vector<bool> usedA(A.size()), usedB(B.size());
            rep(p, A.size()) {
                if (dfs(A[p])) {
                    loopA.push_back(A[p]);
                    usedA[p] = true;
                }
            }
            rep(p, B.size()) {
                if (vis[B[p]]) usedB[p] = true;
            }
            if (loopA.size()) {
                int l = loopA.size();
                rep(j, l) ans.push_back({loopB[j], loopA[(j + 1) % l]});
            }
            vector<int> rA, rB;
            rep(p, A.size()) {
                if (!usedA[p]) rA.push_back(A[p]);
            }
            rep(p, B.size()) {
                if (!usedB[p]) rB.push_back(B[p]);
            }
            if (rA.empty() && rB.empty()) continue;
            if (rA.empty()) rA.push_back(A[0]);
            if (rB.empty()) rB.push_back(B[0]);
            while (rA.size() < rB.size()) rA.push_back(rA.back());
            while (rB.size() < rA.size()) rB.push_back(rB.back());
            rep(j, rA.size()) { ans.push_back({rB[j], rA[j]}); }
        }
    }
    cout << ans.size() << endl;
    for (auto [x, y] : ans) {
        cout << scc[x][0] + 1 << " " << p[scc[y][0]] + 1 << endl;
    }
}
int main() {
    cin.tie(0);
    ios::sync_with_stdio(false);
    int t = 1;
    // cin >> t;
    while (1) solve();
}
accuracy: 0.5
time_ms: 170
memory_kb: 13620
final_rank: 14

code_snippet: #include <bits/stdc++.h>
using namespace std;
using ll = long long;
using ull = unsigned long long;
using uint = unsigned int;
#define rep(i, n) for (int i = 0; i < n; i++)
#define all(v) v.begin(), v.end()
template <class T, class U>
inline bool chmax(T& a, U b) {
    if (a < b) {
        a = b;
        return true;
    }
    return false;
}
template <class T, class U>
inline bool chmin(T& a, U b) {
    if (a > b) {
        a = b;
        return true;
    }
    return false;
}
template <class T>
inline void compress(vector<T>& a) {
    sort(a.begin(), a.end());
    a.erase(unique(a.begin(), a.end()), a.end());
}
template <class T>
T rand(T l, T r) {
    static mt19937 mt(random_device{}());
    // [l, r)
    if constexpr (is_integral_v<T>) {
        return uniform_int_distribution<T>(l, r - 1)(mt);
    } else if constexpr (is_floating_point_v<T>) {
        return uniform_real_distribution<T>(l, r)(mt);
    }
}
constexpr int INF = 1001001001;
constexpr ll llINF = 3000000000000000010;
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>
using namespace __gnu_pbds;
using pbds_set = tree<ll, null_type, less<ll>, rb_tree_tag, tree_order_statistics_node_update>;
using pbds_mset = tree<ll, null_type, less_equal<ll>, rb_tree_tag, tree_order_statistics_node_update>;
using pbds_umap = gp_hash_table<int, int>;
using pbds_trie = trie<string, null_type, trie_string_access_traits<>, pat_trie_tag, trie_prefix_search_node_update>;

#include <algorithm>
#include <cassert>
#include <vector>

namespace atcoder {

struct dsu {
   public:
    dsu() : _n(0) {}
    dsu(int n) : _n(n), parent_or_size(n, -1) {}

    int merge(int a, int b) {
        assert(0 <= a && a < _n);
        assert(0 <= b && b < _n);
        int x = leader(a), y = leader(b);
        if (x == y) return x;
        if (-parent_or_size[x] < -parent_or_size[y]) std::swap(x, y);
        parent_or_size[x] += parent_or_size[y];
        parent_or_size[y] = x;
        return x;
    }

    bool same(int a, int b) {
        assert(0 <= a && a < _n);
        assert(0 <= b && b < _n);
        return leader(a) == leader(b);
    }

    int leader(int a) {
        assert(0 <= a && a < _n);
        if (parent_or_size[a] < 0) return a;
        return parent_or_size[a] = leader(parent_or_size[a]);
    }

    int size(int a) {
        assert(0 <= a && a < _n);
        return -parent_or_size[leader(a)];
    }

    std::vector<std::vector<int>> groups() {
        std::vector<int> leader_buf(_n), group_size(_n);
        for (int i = 0; i < _n; i++) {
            leader_buf[i] = leader(i);
            group_size[leader_buf[i]]++;
        }
        std::vector<std::vector<int>> result(_n);
        for (int i = 0; i < _n; i++) {
            result[i].reserve(group_size[i]);
        }
        for (int i = 0; i < _n; i++) {
            result[leader_buf[i]].push_back(i);
        }
        result.erase(std::remove_if(result.begin(), result.end(), [&](const std::vector<int>& v) { return v.empty(); }), result.end());
        return result;
    }

   private:
    int _n;
    std::vector<int> parent_or_size;
};

}  // namespace atcoder

#include <algorithm>
#include <vector>

namespace atcoder {

namespace internal {

template <class T>
struct simple_queue {
    std::vector<T> payload;
    int pos = 0;
    void reserve(int n) { payload.reserve(n); }
    int size() const { return int(payload.size()) - pos; }
    bool empty() const { return pos == int(payload.size()); }
    void push(const T& t) { payload.push_back(t); }
    T& front() { return payload[pos]; }
    void clear() {
        payload.clear();
        pos = 0;
    }
    void pop() { pos++; }
};

}  // namespace internal

}  // namespace atcoder

#include <cassert>
#include <limits>
#include <queue>
#include <vector>

namespace atcoder {

template <class Cap>
struct mf_graph {
   public:
    mf_graph() : _n(0) {}
    mf_graph(int n) : _n(n), g(n) {}

    int add_edge(int from, int to, Cap cap) {
        assert(0 <= from && from < _n);
        assert(0 <= to && to < _n);
        assert(0 <= cap);
        int m = int(pos.size());
        pos.push_back({from, int(g[from].size())});
        int from_id = int(g[from].size());
        int to_id = int(g[to].size());
        if (from == to) to_id++;
        g[from].push_back(_edge{to, to_id, cap});
        g[to].push_back(_edge{from, from_id, 0});
        return m;
    }

    struct edge {
        int from, to;
        Cap cap, flow;
    };

    edge get_edge(int i) {
        int m = int(pos.size());
        assert(0 <= i && i < m);
        auto _e = g[pos[i].first][pos[i].second];
        auto _re = g[_e.to][_e.rev];
        return edge{pos[i].first, _e.to, _e.cap + _re.cap, _re.cap};
    }
    std::vector<edge> edges() {
        int m = int(pos.size());
        std::vector<edge> result;
        for (int i = 0; i < m; i++) {
            result.push_back(get_edge(i));
        }
        return result;
    }
    void change_edge(int i, Cap new_cap, Cap new_flow) {
        int m = int(pos.size());
        assert(0 <= i && i < m);
        assert(0 <= new_flow && new_flow <= new_cap);
        auto& _e = g[pos[i].first][pos[i].second];
        auto& _re = g[_e.to][_e.rev];
        _e.cap = new_cap - new_flow;
        _re.cap = new_flow;
    }

    Cap flow(int s, int t) { return flow(s, t, std::numeric_limits<Cap>::max()); }
    Cap flow(int s, int t, Cap flow_limit) {
        assert(0 <= s && s < _n);
        assert(0 <= t && t < _n);
        assert(s != t);

        std::vector<int> level(_n), iter(_n);
        internal::simple_queue<int> que;

        auto bfs = [&]() {
            std::fill(level.begin(), level.end(), -1);
            level[s] = 0;
            que.clear();
            que.push(s);
            while (!que.empty()) {
                int v = que.front();
                que.pop();
                for (auto e : g[v]) {
                    if (e.cap == 0 || level[e.to] >= 0) continue;
                    level[e.to] = level[v] + 1;
                    if (e.to == t) return;
                    que.push(e.to);
                }
            }
        };
        auto dfs = [&](auto self, int v, Cap up) {
            if (v == s) return up;
            Cap res = 0;
            int level_v = level[v];
            for (int& i = iter[v]; i < int(g[v].size()); i++) {
                _edge& e = g[v][i];
                if (level_v <= level[e.to] || g[e.to][e.rev].cap == 0) continue;
                Cap d = self(self, e.to, std::min(up - res, g[e.to][e.rev].cap));
                if (d <= 0) continue;
                g[v][i].cap += d;
                g[e.to][e.rev].cap -= d;
                res += d;
                if (res == up) break;
            }
            return res;
        };

        Cap flow = 0;
        while (flow < flow_limit) {
            bfs();
            if (level[t] == -1) break;
            std::fill(iter.begin(), iter.end(), 0);
            while (flow < flow_limit) {
                Cap f = dfs(dfs, t, flow_limit - flow);
                if (!f) break;
                flow += f;
            }
        }
        return flow;
    }

    std::vector<bool> min_cut(int s) {
        std::vector<bool> visited(_n);
        internal::simple_queue<int> que;
        que.push(s);
        while (!que.empty()) {
            int p = que.front();
            que.pop();
            visited[p] = true;
            for (auto e : g[p]) {
                if (e.cap && !visited[e.to]) {
                    visited[e.to] = true;
                    que.push(e.to);
                }
            }
        }
        return visited;
    }

   private:
    int _n;
    struct _edge {
        int to, rev;
        Cap cap;
    };
    std::vector<std::pair<int, int>> pos;
    std::vector<std::vector<_edge>> g;
};

}  // namespace atcoder

#include <algorithm>
#include <utility>
#include <vector>

namespace atcoder {
namespace internal {

template <class E>
struct csr {
    std::vector<int> start;
    std::vector<E> elist;
    csr(int n, const std::vector<std::pair<int, E>>& edges) : start(n + 1), elist(edges.size()) {
        for (auto e : edges) {
            start[e.first + 1]++;
        }
        for (int i = 1; i <= n; i++) {
            start[i] += start[i - 1];
        }
        auto counter = start;
        for (auto e : edges) {
            elist[counter[e.first]++] = e.second;
        }
    }
};

struct scc_graph {
   public:
    scc_graph(int n) : _n(n) {}

    int num_vertices() { return _n; }

    void add_edge(int from, int to) { edges.push_back({from, {to}}); }

    std::pair<int, std::vector<int>> scc_ids() {
        auto g = csr<edge>(_n, edges);
        int now_ord = 0, group_num = 0;
        std::vector<int> visited, low(_n), ord(_n, -1), ids(_n);
        visited.reserve(_n);
        auto dfs = [&](auto self, int v) -> void {
            low[v] = ord[v] = now_ord++;
            visited.push_back(v);
            for (int i = g.start[v]; i < g.start[v + 1]; i++) {
                auto to = g.elist[i].to;
                if (ord[to] == -1) {
                    self(self, to);
                    low[v] = std::min(low[v], low[to]);
                } else {
                    low[v] = std::min(low[v], ord[to]);
                }
            }
            if (low[v] == ord[v]) {
                while (true) {
                    int u = visited.back();
                    visited.pop_back();
                    ord[u] = _n;
                    ids[u] = group_num;
                    if (u == v) break;
                }
                group_num++;
            }
        };
        for (int i = 0; i < _n; i++) {
            if (ord[i] == -1) dfs(dfs, i);
        }
        for (auto& x : ids) {
            x = group_num - 1 - x;
        }
        return {group_num, ids};
    }

    std::vector<std::vector<int>> scc() {
        auto ids = scc_ids();
        int group_num = ids.first;
        std::vector<int> counts(group_num);
        for (auto x : ids.second) counts[x]++;
        std::vector<std::vector<int>> groups(ids.first);
        for (int i = 0; i < group_num; i++) {
            groups[i].reserve(counts[i]);
        }
        for (int i = 0; i < _n; i++) {
            groups[ids.second[i]].push_back(i);
        }
        return groups;
    }

   private:
    int _n;
    struct edge {
        int to;
    };
    std::vector<std::pair<int, edge>> edges;
};

}  // namespace internal

}  // namespace atcoder

#include <cassert>
#include <vector>

namespace atcoder {

struct scc_graph {
   public:
    scc_graph() : internal(0) {}
    scc_graph(int n) : internal(n) {}

    void add_edge(int from, int to) {
        int n = internal.num_vertices();
        assert(0 <= from && from < n);
        assert(0 <= to && to < n);
        internal.add_edge(from, to);
    }

    std::vector<std::vector<int>> scc() { return internal.scc(); }

   private:
    internal::scc_graph internal;
};

}  // namespace atcoder

void solve() {
    int n, m;
    cin >> n >> m;
    if (n == 0 && m == 0) exit(0);
    atcoder::mf_graph<int> g(2 * n + 2);
    vector<int> s(m), t(m);
    rep(i, m) {
        cin >> s[i] >> t[i];
        s[i]--, t[i]--;
        g.add_edge(s[i], t[i] + n, 1);
    }
    rep(i, n) {
        g.add_edge(2 * n, i, 1);
        g.add_edge(i + n, 2 * n + 1, 1);
    }
    assert(g.flow(2 * n, 2 * n + 1) == n);
    vector<int> p(n), ip(n);
    vector<pair<int, int>> rem;
    for (auto [from, to, cap, flow] : g.edges()) {
        if (from == 2 * n || to == 2 * n + 1) continue;
        to -= n;
        if (flow == 1) {
            p[from] = to;
            ip[to] = from;
        } else {
            rem.push_back({from, to});
        }
    }

    // 完全マッチングを0->0,1->1,...と置きなおす
    // 他の辺をu->vと書き直す
    // x->yが固定されたとき、y->xのパスがあればいい
    // 強連結であればよい
    atcoder::scc_graph sg(n);
    for (auto& [x, y] : rem) {
        // x->ip[y]
        y = ip[y];
        sg.add_edge(x, y);
    }
    auto scc = sg.scc();
    int k = scc.size();
    vector<int> group(n);
    rep(i, k) for (int x : scc[i]) group[x] = i;
    vector<int> in(k), out(k);
    atcoder::dsu uf(k);
    vector<bitset<2000>> bs(n);
    for (auto [x, y] : rem) {
        if (group[x] == group[y]) continue;
        bs[group[x]].set(group[y]);
        out[group[x]]++, in[group[y]]++;
        uf.merge(group[x], group[y]);
    }
    for (int i = n - 1; i >= 0; i--) {
        bs[i].set(i);
        for (int j = i + 1; j < n; j++) {
            if (bs[i][j]) bs[i] |= bs[j];
        }
    }
    vector<vector<int>> src(k), snk(k);
    rep(i, k) {
        if (in[i] == 0 && out[i] == 0) continue;
        if (out[i] == 0) snk[uf.leader(i)].push_back(i);
        if (in[i] == 0) src[uf.leader(i)].push_back(i);
    }

    vector<pair<int, int>> ans;
    rep(i, k) {
        if (uf.leader(i) == i) {
            auto A = src[i], B = snk[i];
            if (A.empty()) continue;
            int M = max(snk[i].size(), src[i].size());
            vector<bool> usedA(A.size()), usedB(B.size());
            // src->snkが到達不可能な時snk->srcに貼る
            rep(p, A.size()) {
                rep(q, B.size()) {
                    if (!usedA[p] && !usedB[q] && !bs[A[p]][B[q]]) {
                        ans.push_back({B[q], A[p]});
                        usedA[p] = true;
                        usedB[q] = true;
                    }
                }
            }
            // 今、src->snkはどの対も到達可能
            vector<int> rA, rB;
            rep(p, A.size()) {
                if (!usedA[p]) rA.push_back(A[p]);
            }
            rep(p, B.size()) {
                if (!usedB[p]) rB.push_back(B[p]);
            }
            if (rA.empty()) rA.push_back(A[0]);
            if (rB.empty()) rB.push_back(B[0]);
            while (rA.size() < rB.size()) rA.push_back(rA.back());
            while (rB.size() < rA.size()) rB.push_back(rB.back());
            rep(j, max(rA.size(), rB.size())) { ans.push_back({rB[j], rA[j]}); }
        }
    }
    cout << ans.size() << endl;
    for (auto [x, y] : ans) {
        cout << scc[x][0] + 1 << " " << p[scc[y][0]] + 1 << endl;
    }
}
int main() {
    cin.tie(0);
    ios::sync_with_stdio(false);
    int t = 1;
    // cin >> t;
    while (1) solve();
}
accuracy: 0.5
time_ms: 300
memory_kb: 13244
final_rank: 17

code_snippet: #include <bits/stdc++.h>

using namespace std;

#define rep(i, a, b) for (int i = (int)(a); i < (int)(b); i++)
#define rrep(i, a, b) for (int i = (int)(b)-1; i >= (int)(a); i--)
#define ALL(v) (v).begin(), (v).end()
#define UNIQUE(v) sort(ALL(v)), (v).erase(unique(ALL(v)), (v).end())
#define SZ(v) (int)v.size()
#define MIN(v) *min_element(ALL(v))
#define MAX(v) *max_element(ALL(v))
#define LB(v, x) int(lower_bound(ALL(v), (x)) - (v).begin())
#define UB(v, x) int(upper_bound(ALL(v), (x)) - (v).begin())

using uint = unsigned int;
using ll = long long int;
using ull = unsigned long long;
using i128 = __int128_t;
using u128 = __uint128_t;
const int inf = 0x3fffffff;
const ll INF = 0x1fffffffffffffff;

template <typename T> inline bool chmax(T &a, T b) {
    if (a < b) {
        a = b;
        return 1;
    }
    return 0;
}
template <typename T> inline bool chmin(T &a, T b) {
    if (a > b) {
        a = b;
        return 1;
    }
    return 0;
}
template <typename T, typename U> T ceil(T x, U y) {
    assert(y != 0);
    if (y < 0)
        x = -x, y = -y;
    return (x > 0 ? (x + y - 1) / y : x / y);
}
template <typename T, typename U> T floor(T x, U y) {
    assert(y != 0);
    if (y < 0)
        x = -x, y = -y;
    return (x > 0 ? x / y : (x - y + 1) / y);
}
template <typename T> int popcnt(T x) {
    return __builtin_popcountll(x);
}
template <typename T> int topbit(T x) {
    return (x == 0 ? -1 : 63 - __builtin_clzll(x));
}
template <typename T> int lowbit(T x) {
    return (x == 0 ? -1 : __builtin_ctzll(x));
}

template <class T, class U>
ostream &operator<<(ostream &os, const pair<T, U> &p) {
    os << "P(" << p.first << ", " << p.second << ")";
    return os;
}
template <typename T> ostream &operator<<(ostream &os, const vector<T> &vec) {
    os << "{";
    for (int i = 0; i < vec.size(); i++) {
        os << vec[i] << (i + 1 == vec.size() ? "" : ", ");
    }
    os << "}";
    return os;
}
template <typename T, typename U>
ostream &operator<<(ostream &os, const map<T, U> &map_var) {
    os << "{";
    for (auto itr = map_var.begin(); itr != map_var.end(); itr++) {
        os << "(" << itr->first << ", " << itr->second << ")";
        itr++;
        if (itr != map_var.end())
            os << ", ";
        itr--;
    }
    os << "}";
    return os;
}
template <typename T> ostream &operator<<(ostream &os, const set<T> &set_var) {
    os << "{";
    for (auto itr = set_var.begin(); itr != set_var.end(); itr++) {
        os << *itr;
        ++itr;
        if (itr != set_var.end())
            os << ", ";
        itr--;
    }
    os << "}";
    return os;
}
#ifdef LOCAL
#define show(...) _show(0, #__VA_ARGS__, __VA_ARGS__)
#else
#define show(...) true
#endif
template <typename T> void _show(int i, T name) {
    cerr << '\n';
}
template <typename T1, typename T2, typename... T3>
void _show(int i, const T1 &a, const T2 &b, const T3 &...c) {
    for (; a[i] != ',' && a[i] != '\0'; i++)
        cerr << a[i];
    cerr << ":" << b << " ";
    _show(i + 1, a, c...);
}

/**
* @brief template
*/

vector<int> BipMatch(int n,int m,vector<vector<int>>& g){
    vector<int> L(n,-1),R(m,-1),d(n);
    queue<int> que;
    auto dfs=[&](auto& dfs,int v)->bool{
        int nd=exchange(d[v],0)+1;
        for(auto& u:g[v]){
            if(R[u]==-1 or (d[R[u]]==nd and dfs(dfs,R[u]))){
                L[v]=u,R[u]=v;
                return 1;
            }
        }
        return 0;
    };
    for(;;){
        d.assign(n,0);
        queue<int> dummy;
        swap(que,dummy);
        bool ch=0;   
        rep(i,0,n)if(L[i]==-1){
            que.push(i);
            d[i]=1;
        }
        while(!que.empty()){
            int v=que.front();
            que.pop();
            for(auto& u:g[v]){
                if(R[u]==-1)ch=1;
                else if(!d[R[u]]){
                    d[R[u]]=d[v]+1;
                    que.push(R[u]);
                }
            }
        }
        if(!ch)break;
        rep(i,0,n)if(L[i]==-1)dfs(dfs,i);
    }
    return L;
}

/**
 * @brief Bipartite Matching
 */

 struct SCC{
    int n,m,cur;
    vector<vector<int>> g;
    vector<int> low,ord,id;
    SCC(int _n=0):n(_n),m(0),cur(0),g(_n),low(_n),ord(_n,-1),id(_n){}
    void resize(int _n){
        n=_n;
        g.resize(n);
        low.resize(n);
        ord.resize(n,-1);
        id.resize(n);
    }
    void add_edge(int u,int v){g[u].emplace_back(v);}
    void dfs(int v,vector<int>& used){
        ord[v]=low[v]=cur++;
        used.emplace_back(v);
        for(auto& nxt:g[v]){
            if(ord[nxt]==-1){
                dfs(nxt,used); chmin(low[v],low[nxt]);
            }
            else{
                chmin(low[v],ord[nxt]);
            }
        }
        if(ord[v]==low[v]){
            while(1){
                int add=used.back(); used.pop_back();
                ord[add]=n; id[add]=m;
                if(v==add)break;
            }
            m++;
        }
    }
    void run(){
        vector<int> used;
        rep(v,0,n)if(ord[v]==-1)dfs(v,used);
        for(auto& x:id)x=m-1-x;
    }
};

/**
 * @brief Strongly Connected Components
 */

int main() {
    cin.tie(0);
    ios_base::sync_with_stdio(false);
while(1) {
    int N, M;
    cin >> N >> M;
    if (N == 0) return 0;
    vector<int> S(M), T(M);
    vector<vector<int>> G(N);
    rep(i,0,M) cin >> S[i] >> T[i], S[i]--, T[i]--, G[S[i]].push_back(T[i]);
    vector<int> BM = BipMatch(N,N,G);
    SCC scc(N*2);
    rep(i,0,M) {
        if (BM[S[i]] == T[i]) {
            scc.add_edge(S[i], T[i]+N);
            scc.add_edge(T[i]+N, S[i]);
        }
        else {
            scc.add_edge(S[i], T[i]+N);
        }
    }
    scc.run();
    int K = scc.m;
    vector<vector<int>> H(K), RH(K);
    vector<int> SID(K), TID(K);
    rrep(i,0,N) {
        SID[scc.id[i]] = i;
        TID[scc.id[i+N]] = i;
    }
    rep(i,0,M) {
        if (scc.id[S[i]] != scc.id[T[i]+N]) {
            H[scc.id[S[i]]].push_back(scc.id[T[i]+N]);
            RH[scc.id[T[i]+N]].push_back(scc.id[S[i]]);
        }
    }
    vector<int> Source, Sink;
    rep(i,0,K) {
        if (H[i].empty() && RH[i].empty()) continue;
        if (H[i].empty()) Sink.push_back(i);
        if (RH[i].empty()) Source.push_back(i);
    }

    // cout << "Source:";
    // for (int so : Source) cout << so << ' ';
    // cout << endl;
    // cout << "Sink:";
    // for (int si : Sink) cout << si << ' ';
    // cout << endl;

    vector<bool> vis(K, false);
    auto findsink = [&](auto self, int V) -> int {
        vis[V] = true;
        if (H[V].empty()) return V;
        for (int NV : H[V]) {
            if (vis[NV]) continue;
            int ret = self(self, NV);
            if (ret >= 0) return ret;
        }
        return -1;
    };
    vector<pair<int,int>> P;
    vector<bool> soflag(K, false), siflag(K, false);
    for (int so : Source) {
        int ret = findsink(findsink, so);
        //cout << "findsink:" << so << ' ' << ret << endl;
        if (ret >= 0) P.push_back({so, ret}), soflag[so] = siflag[ret] = true;
    }
    vector<pair<int,int>> ANS;
    rep(i,0,SZ(P)-1) {
        ANS.push_back({P[i+1].second, P[i].first});
    }
    if (!P.empty()) ANS.push_back({P[0].second, P.back().first});

    vector<int> ConID(K,-1);
    auto DFS = [&](auto self, int V, int id) -> void {
        ConID[V] = id;
        for (int NV : H[V]) {
            if (ConID[NV] == -1) self(self, NV, id);
        }
        for (int NV : RH[V]) {
            if (ConID[NV] == -1) self(self, NV, id);
        }
    };

    int L = 0;
    rep(i,0,K) {
        if (ConID[i] == -1) DFS(DFS,i,L), L++;
    }

    vector<int> unso, unsi;
    for (int so : Source) if (!soflag[so]) unso.push_back(so);
    for (int si : Sink) if (!siflag[si]) unsi.push_back(si);

    // cout << "SID:";
    // for (int sid : SID) cout << sid << ' ';
    // cout << endl;
    // cout << "TID:";
    // for (int tid : TID) cout << tid << ' ';
    // cout << endl;
    // cout << "Unso:";
    // for (int si : unso) cout << si << ' ';
    // cout << endl;
    // cout << "Unsi:";
    // for (int si : unsi) cout << si << ' ';
    // cout << endl << endl;

    while(!unso.empty() || !unsi.empty()) {
        if (unso.empty()) {
            ANS.push_back({unsi.back(), Source[0]});
            unsi.pop_back();
        }
        else if (unsi.empty()) {
            ANS.push_back({Sink[0], unso.back()});
            unso.pop_back();
        }
        else {
            ANS.push_back({unsi.back(), unso.back()});
            unso.pop_back();
            unsi.pop_back();
        }
    }
    cout << SZ(ANS) << endl;
    for (auto [x,y] : ANS) {
        cout << SID[x] + 1 << ' ' << TID[y] + 1 << endl;
    }
}
}
accuracy: 1
time_ms: 120
memory_kb: 7396
final_rank: 1

code_snippet: #include <bits/stdc++.h>
using namespace std;

using ll = long long;

mt19937 engine(42);

template<class flow_t = int>
struct Dinic
{
    const flow_t INF;
    
    struct edge
    {
        int to;
        flow_t cap;
        int rev; 
        bool isrev;
        int idx;
    };
    
    vector<vector<edge>> graph;
    vector<int> min_cost, iter;
    
    explicit Dinic(int V) : INF(1e9), graph(V) { }
    
    void add_edge(int from, int to, flow_t cap, int idx = -1)
    {
        graph[from].emplace_back((edge){to, cap, (int)graph[to].size(), false, idx});
        graph[to].emplace_back((edge){from, 0, (int)graph[from].size() - 1, true, idx});
    }
    
    bool build_augment_path(int s, int t)
    {
        min_cost.assign(graph.size(), -1);
        queue<int> que;
        min_cost[s] = 0;
        que.push(s);
        while (!que.empty() && min_cost[t] == -1)
        {
            int p = que.front();
            que.pop();
            
            for (auto &e : graph[p])
            {
                if (e.cap > 0 && min_cost[e.to] == -1)
                {
                    min_cost[e.to] = min_cost[p] + 1;
                    que.push(e.to);
                }
            }
        }
        return min_cost[t] != -1;
    }
    
    flow_t find_min_dist_augment_path(int idx, const int t, flow_t flow)
    {
        if (idx == t) return flow;
        for (int &i = iter[idx]; i < (int)graph[idx].size(); ++i)
        {
            edge &e = graph[idx][i];
            if (e.cap > 0 && min_cost[idx] < min_cost[e.to])
            {
                flow_t d = find_min_dist_augment_path(e.to, t, min(flow, e.cap));
                if (d > 0)
                {
                    e.cap -= d;
                    graph[e.to][e.rev].cap += d;
                    return d;
                }
            }
        }
        return 0;
    }
    
    flow_t max_flow(int s, int t)
    {
        flow_t flow = 0;
        while (build_augment_path(s, t))
        {
            iter.assign(graph.size(), 0);
            flow_t f;
            while ((f = find_min_dist_augment_path(s, t, INF)) > 0) flow += f;
        }
        return flow;
    }
    
};

struct SCC {
    using G = vector<vector<int>>;
        
    int n;
    const G &g;
    G edge, redge;
    vector<int> comp, order, used;  // comp にグループの番号が振られる

    SCC(int n, G &g) : n(n), g(g), edge(n), redge(n), comp(n, -1), used(n) {
        for (int i = 0; i < n; i++) {
            for (auto e : g[i]) {
                edge[i].emplace_back(e);
                redge[e].emplace_back(i);
            }
        }
    }

    void dfs(int idx) {
        if (used[idx]) return;
        used[idx] = true;
        for (int to : edge[idx]) dfs(to);
        order.emplace_back(idx);
    }

    void rdfs(int idx, int cnt) {
        if (comp[idx] != -1) return;
        comp[idx] = cnt;
        for (int to : redge[idx]) rdfs(to, cnt);
    }

    G build() {
        // 強連結成分ごとに，トポソ順で返す
        G t;
        for (int i = 0; i < n; i++) dfs(i);
        reverse(order.begin(), order.end());
        int ptr = 0;
        for (int i : order) {
            if (comp[i] == -1) {
                rdfs(i, ptr), ptr++;
            }
        }

        t.resize(ptr);
        for (int i = 0; i < n; i++) {
            t[comp[i]].emplace_back(i);
        }
        return t;
    }
};



bool is_end = false;

void solve()
{
    int N, M; cin >> N >> M;
    if (N == 0 && M == 0)
    {
        is_end = true;
        return;
    }
    
    int S = 2 * N, T = S + 1;
    Dinic<int> mf(T + 1);
    vector<array<int, 2>> edge;
    for (int i = 0; i < M; ++i)
    {
        int s, t; cin >> s >> t;
        s--, t--;
        mf.add_edge(s, N + t, 1);
        edge.push_back({s, N + t});
    }
    for (int i = 0; i < N; ++i)
    {
        mf.add_edge(S, i, 1);
        mf.add_edge(N + i, T, 1);
    }
    
    int F = mf.max_flow(S, T);
    assert(F == N);
    
    vector<int> matching(2 * N, -1);
    for (int s = 0; s < N; ++s)
    {
        for (auto e : mf.graph[s])
        {
            if (e.cap == 0 && e.to != S)
            {
                int t = e.to;
                assert(N <= t && t < 2 * N);
                matching[s] = t;
                matching[t] = s;
                break;
            }
        }
    }
    
    for (int i = 0; i < 2 * N; ++i)
    {
        int m = matching[i];
        if (i < N) assert(N <= m && m < 2 * N);
        else assert(0 <= m && m < N);
    }
    
    vector<vector<int>> graph(N);
    for (auto [s, t] : edge)
    {
        int ns = matching[t];
        if (s != ns) graph[s].emplace_back(ns);
    }
    
    SCC scc(N, graph);
    vector<vector<int>> components = scc.build();
    vector<int> v_to_comp(N, -1);
    for (int i = 0; i < components.size(); ++i)
    {
        for (auto v : components[i])
        {
            v_to_comp[v] = i;
        }
    }
    
    int C = components.size();
    vector<vector<int>> dag(C);
    vector<vector<int>> rev(C);
    for (auto [s, t] : edge)
    {
        int ns = matching[t];
        int sid = v_to_comp[s], nsid = v_to_comp[ns];
        if (sid != nsid)
        {
            dag[sid].emplace_back(nsid);
            rev[nsid].emplace_back(sid);
        }
    }
    for (auto &vec : dag)
    {
        sort(vec.begin(), vec.end());
        vec.erase(unique(vec.begin(), vec.end()), vec.end());
    }
    for (auto &vec : rev)
    {
        sort(vec.begin(), vec.end());
        vec.erase(unique(vec.begin(), vec.end()), vec.end());
    }
    
    using bs = bitset<2048>;
    vector<bs> in(C, 0), out(C, 0);
    vector<int> invec, outvec;
    for (int i = 0; i < C; ++i)
    {
        if (rev[i].size() == 0 && dag[i].size() > 0) invec.emplace_back(i);
        if (dag[i].size() == 0 && rev[i].size() > 0) outvec.emplace_back(i);
        in[i][i] = 1;
        for (auto nv : dag[i]) in[nv] |= in[i];
    }
    for (int i = C - 1; i >= 0; --i)
    {
        out[i][i] = 1;
        for (auto nv : dag[i]) out[i] |= out[nv];
    }
    
    // cout << -1 << endl;
    // for (int i = 0; i < C; ++i)
    // {
    //     cout << i << " : ";
    //     for (auto v : components[i]) cout << v << " ";
    //     cout << endl;
    // }
    // for (int i = 0; i < C; ++i)
    // {
    //     cout << in[i] << " " << out[i] << endl;
    // }
    
    vector<array<int, 2>> res;
    
    int K = max(invec.size(), outvec.size());
    
    while (K > 0)
    {
        int sid = engine() % invec.size();
        int tid = engine() % outvec.size();
        
        int s = invec[sid];
        int t = outvec[tid];
        
        bool keeps = false, keept = false;
        
        if (K == 1)
        {
            res.push_back({t, s});
            break;
        }
        
        // out[s] > in[t]  <=>  t->s is in
        if ((out[s] & in[t]) == in[t])
        {
            if (invec.size() >= outvec.size()) continue;
            keeps = true;
        }
        
        // out[s] < In[t]  <=>  t->s is out
        if ((out[s] & in[t]) == out[s])
        {
            if (invec.size() <= outvec.size()) continue;
            keept = true;
        }
        
        dag[t].emplace_back(s);
        rev[s].emplace_back(t);
        res.push_back({t, s});
        if (!keeps) invec.erase(invec.begin() + sid);
        if (!keept) outvec.erase(outvec.begin() + tid);
        
        // cout << s << " " << t << endl;
        // cout << t << " " << matching[s] - N << endl;
        
        {
            in[s] |= in[t];
            out[t] |= out[s];
            for (int i = 0; i < C; ++i)
            {
                if (out[s][i]) in[i] |= in[s];
                if (in[t][i]) out[i] |= out[t];
            }
        }
        
        K--;
        assert(max(invec.size(), outvec.size()) == K);
    }
    
    
    cout << res.size() << endl;
    for (auto [a, b] : res)
    {
        int s = components[a][0];
        int t = matching[components[b][0]];
        cout << s + 1 << " " << t - N + 1 << endl;
    }
    
    return;
}

int main()
{
    while (!is_end)
    {
        solve();
    }
    
    return 0;
}
accuracy: 1
time_ms: 380
memory_kb: 15168
final_rank: 8

code_snippet: #include <bits/stdc++.h>
using namespace std;

using ll = long long;

mt19937 engine(42);

template<class flow_t = int>
struct Dinic
{
    const flow_t INF;
    
    struct edge
    {
        int to;
        flow_t cap;
        int rev; 
        bool isrev;
        int idx;
    };
    
    vector<vector<edge>> graph;
    vector<int> min_cost, iter;
    
    explicit Dinic(int V) : INF(1e9), graph(V) { }
    
    void add_edge(int from, int to, flow_t cap, int idx = -1)
    {
        graph[from].emplace_back((edge){to, cap, (int)graph[to].size(), false, idx});
        graph[to].emplace_back((edge){from, 0, (int)graph[from].size() - 1, true, idx});
    }
    
    bool build_augment_path(int s, int t)
    {
        min_cost.assign(graph.size(), -1);
        queue<int> que;
        min_cost[s] = 0;
        que.push(s);
        while (!que.empty() && min_cost[t] == -1)
        {
            int p = que.front();
            que.pop();
            
            for (auto &e : graph[p])
            {
                if (e.cap > 0 && min_cost[e.to] == -1)
                {
                    min_cost[e.to] = min_cost[p] + 1;
                    que.push(e.to);
                }
            }
        }
        return min_cost[t] != -1;
    }
    
    flow_t find_min_dist_augment_path(int idx, const int t, flow_t flow)
    {
        if (idx == t) return flow;
        for (int &i = iter[idx]; i < (int)graph[idx].size(); ++i)
        {
            edge &e = graph[idx][i];
            if (e.cap > 0 && min_cost[idx] < min_cost[e.to])
            {
                flow_t d = find_min_dist_augment_path(e.to, t, min(flow, e.cap));
                if (d > 0)
                {
                    e.cap -= d;
                    graph[e.to][e.rev].cap += d;
                    return d;
                }
            }
        }
        return 0;
    }
    
    flow_t max_flow(int s, int t)
    {
        flow_t flow = 0;
        while (build_augment_path(s, t))
        {
            iter.assign(graph.size(), 0);
            flow_t f;
            while ((f = find_min_dist_augment_path(s, t, INF)) > 0) flow += f;
        }
        return flow;
    }
    
};

bool is_end = false;

void solve()
{
    int N, M; cin >> N >> M;
    if (N == 0 && M == 0)
    {
        is_end = true;
        return;
    }
    
    vector<int> deg(2 * N, 0);
    int S = 2 * N, T = S + 1;
    Dinic<int> mf(T + 1);
    for (int i = 0; i < M; ++i)
    {
        int s, t; cin >> s >> t;
        s--, t--;
        mf.add_edge(s, N + t, 1);
        deg[s] += 1;
        deg[N + t] += 1;
    }
    for (int i = 0; i < N; ++i)
    {
        mf.add_edge(S, i, 1);
        mf.add_edge(N + i, T, 1);
    }
    
    int F = mf.max_flow(S, T);
    assert(F == N);
    
    vector<int> matching(2 * N, -1);
    for (int s = 0; s < N; ++s)
    {
        for (auto e : mf.graph[s])
        {
            if (e.cap == 0 && e.to != S)
            {
                int t = e.to;
                assert(N <= t && t < 2 * N);
                matching[s] = t;
                matching[t] = s;
                break;
            }
        }
    }
    
    for (int i = 0; i < 2 * N; ++i)
    {
        int m = matching[i];
        if (i < N) assert(N <= m && m < 2 * N);
        else assert(0 <= m && m < N);
    }
    
    vector<int> left, right;
    vector<int> free_left, free_right;
    for (int s = 0; s < N; ++s)
    {
        int t = matching[s];
        if (deg[s] == 1 && deg[t] == 1) continue;
        
        if (deg[s] == 1)
        {
            left.emplace_back(s);
            free_right.emplace_back(t);
        }
        else if (deg[t] == 1)
        {
            right.emplace_back(t);
            free_left.emplace_back(s);
        }
        else
        {
            free_left.emplace_back(s);
            free_right.emplace_back(t);
        }
    }
    
    
    vector<pair<int, int>> res;
    if (left.size() < right.size())
    {
        for (int i = 0; i < left.size(); ++i)
        {
            res.push_back({left[i], right[i]});
        }
        
        int siz = free_left.size();
        for (int i = left.size(); i < right.size(); ++i)
        {
            int s = matching[right[i]];
            
            while (s == matching[right[i]])
            {
                int id = engine() % siz;
                s = free_left[id];
            }
            
            res.push_back({s, right[i]});
        }
        
    }
    else
    {
        for (int i = 0; i < right.size(); ++i)
        {
            res.push_back({left[i], right[i]});
        }
        
        int siz = free_right.size();
        for (int i = right.size(); i < left.size(); ++i)
        {
            int t = matching[left[i]];
            
            while (t == matching[left[i]])
            {
                int id = engine() % siz;
                t = free_right[id];
            }
            
            res.push_back({left[i], t});
        }
        
    }
    
    assert(res.size() == max(left.size(), right.size()));
    cout << res.size() << endl;
    for (auto [s, t] : res)
    {
        cout << s + 1 << " " << t - N + 1 << endl;
    }
    
    return;
}

int main()
{
    while (!is_end)
    {
        solve();
    }
    
    return 0;
}
accuracy: 0.5
time_ms: 290
memory_kb: 10216
final_rank: 16

code_snippet: #include <bits/stdc++.h>
using namespace std;

using ll = long long;

mt19937 engine(42);

template<class flow_t = int>
struct Dinic
{
    const flow_t INF;
    
    struct edge
    {
        int to;
        flow_t cap;
        int rev; 
        bool isrev;
        int idx;
    };
    
    vector<vector<edge>> graph;
    vector<int> min_cost, iter;
    
    explicit Dinic(int V) : INF(1e9), graph(V) { }
    
    void add_edge(int from, int to, flow_t cap, int idx = -1)
    {
        graph[from].emplace_back((edge){to, cap, (int)graph[to].size(), false, idx});
        graph[to].emplace_back((edge){from, 0, (int)graph[from].size() - 1, true, idx});
    }
    
    bool build_augment_path(int s, int t)
    {
        min_cost.assign(graph.size(), -1);
        queue<int> que;
        min_cost[s] = 0;
        que.push(s);
        while (!que.empty() && min_cost[t] == -1)
        {
            int p = que.front();
            que.pop();
            
            for (auto &e : graph[p])
            {
                if (e.cap > 0 && min_cost[e.to] == -1)
                {
                    min_cost[e.to] = min_cost[p] + 1;
                    que.push(e.to);
                }
            }
        }
        return min_cost[t] != -1;
    }
    
    flow_t find_min_dist_augment_path(int idx, const int t, flow_t flow)
    {
        if (idx == t) return flow;
        for (int &i = iter[idx]; i < (int)graph[idx].size(); ++i)
        {
            edge &e = graph[idx][i];
            if (e.cap > 0 && min_cost[idx] < min_cost[e.to])
            {
                flow_t d = find_min_dist_augment_path(e.to, t, min(flow, e.cap));
                if (d > 0)
                {
                    e.cap -= d;
                    graph[e.to][e.rev].cap += d;
                    return d;
                }
            }
        }
        return 0;
    }
    
    flow_t max_flow(int s, int t)
    {
        flow_t flow = 0;
        while (build_augment_path(s, t))
        {
            iter.assign(graph.size(), 0);
            flow_t f;
            while ((f = find_min_dist_augment_path(s, t, INF)) > 0) flow += f;
        }
        return flow;
    }
    
};

bool is_end = false;

void solve()
{
    int N, M; cin >> N >> M;
    if (N == 0 && M == 0)
    {
        is_end = true;
        return;
    }
    
    vector<int> deg(2 * N, 0);
    int S = 2 * N, T = S + 1;
    Dinic<int> mf(T + 1);
    for (int i = 0; i < M; ++i)
    {
        int s, t; cin >> s >> t;
        s--, t--;
        mf.add_edge(s, N + t, 1);
        deg[s] += 1;
        deg[N + t] += 1;
    }
    for (int i = 0; i < N; ++i)
    {
        mf.add_edge(S, i, 1);
        mf.add_edge(N + i, T, 1);
    }
    
    int F = mf.max_flow(S, T);
    assert(F == N);
    
    vector<int> matching(2 * N, -1);
    for (int s = 0; s < N; ++s)
    {
        for (auto e : mf.graph[s])
        {
            if (e.cap == 0 && e.to != S)
            {
                int t = e.to;
                assert(N <= t && t < 2 * N);
                matching[s] = t;
                matching[t] = s;
                break;
            }
        }
    }
    
    vector<int> left, right;
    vector<int> free_left, free_right;
    for (int s = 0; s < N; ++s)
    {
        int t = matching[s];
        if (deg[s] == 1 && deg[t] == 1) continue;
        if (deg[s] == 1)
        {
            left.emplace_back(s);
            free_right.emplace_back(t);
        }
        if (deg[t] == 1)
        {
            right.emplace_back(t);
            free_left.emplace_back(s);
        }
    }
    
    
    vector<pair<int, int>> res;
    if (left.size() < right.size())
    {
        for (int i = 0; i < left.size(); ++i)
        {
            res.push_back({left[i], right[i]});
        }
        
        int siz = free_left.size();
        for (int i = left.size(); i < right.size(); ++i)
        {
            int s = matching[right[i]];
            
            while (s == matching[right[i]])
            {
                int id = engine() % siz;
                s = free_left[id];
            }
            
            res.push_back({s, right[i]});
        }
        
    }
    else
    {
        for (int i = 0; i < right.size(); ++i)
        {
            res.push_back({left[i], right[i]});
        }
        
        int siz = free_right.size();
        for (int i = right.size(); i < left.size(); ++i)
        {
            int t = matching[left[i]];
            
            while (t == matching[left[i]])
            {
                int id = engine() % siz;
                t = free_right[id];
            }
            
            res.push_back({left[i], t});
        }
        
    }
    
    cout << res.size() << endl;
    for (auto [s, t] : res)
    {
        cout << s + 1 << " " << t - N + 1 << endl;
    }
    
    return;
}

int main()
{
    while (!is_end)
    {
        solve();
    }
    
    return 0;
}
accuracy: 0.5
time_ms: 290
memory_kb: 10196
final_rank: 15

code_snippet: #line 2 "cp-library/src/cp-template.hpp"
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using ld = long double;
using uint = unsigned int;
using ull  = unsigned long long;
using i32 = int;
using u32 = unsigned int;
using i64 = long long;
using u64 = unsigned long long;
using i128 = __int128_t;
template < class T > bool chmin(T& a, T b) { if(a > b) { a = b; return true; } return false; }
template < class T > bool chmax(T& a, T b) { if(a < b) { a = b; return true; } return false; }
template < class T, class U > T ceil (T x, U y) { return (x > 0 ? (x + y - 1) / y :           x / y); }
template < class T, class U > T floor(T x, U y) { return (x > 0 ?           x / y : (x - y + 1) / y); }
int popcnt(i32 x) { return __builtin_popcount(x); }
int popcnt(u32 x) { return __builtin_popcount(x); }
int popcnt(i64 x) { return __builtin_popcountll(x); }
int popcnt(u64 x) { return __builtin_popcountll(x); }

#line 2 "cp-library/src/utility/rep_itr.hpp"
template < class T > struct itr_rep {
    T i, d;
    constexpr itr_rep(const T i) noexcept : i(i), d(1) {}
    constexpr itr_rep(const T i, const T d) noexcept : i(i), d(d) {}
    void operator++() noexcept { i += d; }
    constexpr int operator*() const noexcept { return i; }
    constexpr bool operator!=(const itr_rep x) const noexcept { return d > 0 ? i < x.i : i > x.i; }
};

template < class T > struct rep {
    const itr_rep< T > s, t;
    constexpr rep(const T t) noexcept : s(0), t(t) {}
    constexpr rep(const T s, const T t) noexcept : s(s), t(t) {}
    constexpr rep(const T s, const T t, const T d) noexcept : s(s, d), t(t, d) {}
    constexpr auto begin() const noexcept { return s; }
    constexpr auto end  () const noexcept { return t; }
};

template < class T > struct revrep {
    const itr_rep < T > s, t;
    constexpr revrep(const T t) noexcept : s(t - 1, -1), t(-1, -1) {}
    constexpr revrep(const T s, const T t) noexcept : s(t - 1, -1), t(s - 1, -1) {}
    constexpr revrep(const T s, const T t, const T d) noexcept : s(t - 1, -d), t(s - 1, -d) {}
    constexpr auto begin() const noexcept { return s; }
    constexpr auto end  () const noexcept { return t; }
};
#line 3 "cp-library/src/utility/io.hpp"

/* 128bit integer */
istream& operator>>(istream& is, i128& x) {
    std::string s; is >> s;
    int pm = (s[0] == '-');
    x = 0;
    for(int i : rep(pm, int(s.size()))) x = x * 10 + (s[i] - '0');
    if(pm) x *= -1;
    return is;
}
ostream& operator<<(ostream& os, const i128& x) {
    if(x == 0) return os << '0';
    i128 y = x;
    if(y < 0) { os << '-'; y *= -1; }
    std::vector<int> ny;
    while(y > 0) { ny.push_back(y % 10); y /= 10; }
    for(int i : revrep(ny.size())) os << ny[i];
    return os;
}

template < class S, class T > istream& operator>>(istream& is,       std::pair< S, T >& x) { is >> x.first        >> x.second; return is; }
template < class S, class T > ostream& operator<<(ostream& os, const std::pair< S, T >& x) { os << x.first << " " << x.second; return os; }

namespace scanner {
    struct sca {
        template < class T > operator T() {
            T s; std::cin >> s; return s;
        }
    };
    struct vec {
        int n;
        vec(int n) : n(n) {}
        template < class T > operator std::vector< T >() {
            std::vector< T > v(n);
            for(T& x : v) std::cin >> x;
            return v;
        }
    };
    struct mat {
        int h, w;
        mat(int h, int w) : h(h), w(w) {}
        template < class T > operator std::vector< std::vector< T > >() {
            std::vector m(h, std::vector< T >(w));
            for(std::vector< T >& v : m) for(T& x : v) std::cin >> x;
            return m;
        }
    };
    struct speedup {
        speedup() {
            std::cin.tie(0);
            std::ios::sync_with_stdio(0);
        }
    } speedup_instance;
}
scanner::sca in() { return scanner::sca(); }
scanner::vec in(int n) { return scanner::vec(n); }
scanner::mat in(int h, int w) { return scanner::mat(h, w); }

namespace printer {
    void precision(int d) { std::cout << std::fixed << std::setprecision(d); }
    void flush() { std::cout.flush(); }
}

template < class T >
ostream& operator<<(ostream& os, const std::vector< T > a) {
    int n = a.size();
    for(int i : rep(n)) { os << a[i]; if(i != n - 1) os << ' '; }
    return os;
}

int print() { std::cout << '\n'; return 0; }
template < class head, class... tail > int print(head&& h, tail&&... t) {
    std::cout << h; if(sizeof...(tail)) std::cout << ' ';
    return print(std::forward<tail>(t)...);
}
template < class T > int print_n(const std::vector< T > a) {
    int n = a.size();
    for(int i : rep(n)) std::cout << a[i] << "\n";
    return 0;
}


#line 2 "cp-library/src/utility/key_val.hpp"

template < class K, class V >
struct key_val {
    K key; V val;
    key_val() {}
    key_val(K key, V val) : key(key), val(val) {}
    template < std::size_t Index >
    std::tuple_element_t< Index, key_val >& get() {
        if constexpr (Index == 0) return key;
        if constexpr (Index == 1) return val;
    }
};

namespace std {

template < class K, class V > struct tuple_size < key_val< K, V > > : integral_constant< size_t, 2 > {};
template < class K, class V > struct tuple_element < 0, key_val< K, V > > { using type = K; };
template < class K, class V > struct tuple_element < 1, key_val< K, V > > { using type = V; };

}
#line 2 "cp-library/src/utility/vec_op.hpp"
template < class T > key_val< int, T > max_of(const vector< T >& a) {
    int i = std::max_element(a.begin(), a.end()) - a.begin();
    return {i, a[i]};
}
template < class T > key_val< int, T > min_of(const vector< T >& a) {
    int i = std::min_element(a.begin(), a.end()) - a.begin();
    return {i, a[i]};
}
template < class S, class T > S sum_of(const vector< T >& a) {
    S sum = 0;
    for(const T x : a) sum += x;
    return sum;
}
template < class S, class T > vector< S > freq_of(const vector< T >& a, T L, T R) {
    vector< S > res(R - L, S(0));
    for(const T x : a) res[x - L] += 1;
    return res;
}
template < class S, class T > struct prefix_sum {
    vector< S > s;
    prefix_sum(const vector< T >& a) : s(a) {
        s.insert(s.begin(), S(0));
        for(int i : rep(a.size())) s[i + 1] += s[i];
    }
    // [L, R)
    S sum(int L, int R) { return s[R] - s[L]; }
};
#line 3 "cp-library/src/utility/heap.hpp"

template < class T > using heap_min = std::priority_queue< T, std::vector< T >, std::greater< T > >;
template < class T > using heap_max = std::priority_queue< T, std::vector< T >, std::less< T > >;

#line 27 "cp-library/src/cp-template.hpp"

#line 1 "cp-library/src/algorithm/bin_search.hpp"
template < class T, class F >
T bin_search(T ok, T ng, F f) {
    while(abs(ng - ok) > 1) {
        T mid = (ok + ng) / 2;
        (f(mid) ? ok : ng) = mid;
    }
    return ok;
}

template < class T, class F >
T bin_search_real(T ok, T ng, F f, int step = 80) {
    while(step--) {
        T mid = (ok + ng) / 2;
        (f(mid) ? ok : ng) = mid;
    }
    return ok;
}
#line 2 "cp-library/src/algorithm/argsort.hpp"

template < class T > std::vector< int > argsort(const std::vector< T > &a) {
    std::vector< int > ids((int)a.size());
    std::iota(ids.begin(), ids.end(), 0);
    std::sort(ids.begin(), ids.end(), [&](int i, int j) {
        return a[i] < a[j] || (a[i] == a[j] && i < j);
    });
    return ids;
}
#line 1 "macro.hpp"
namespace macro {

using size_type = int;
template < class container > void  sort(container& a) { std::sort(std:: begin(a), std:: end(a)); }
template < class container > void rsort(container& a) { std::sort(std::rbegin(a), std::rend(a)); }
template < class container > void reverse(container& a) { std::reverse(std::begin(a), std::end(a)); }
template < class container > void unique(container& a) {
    std::sort(std::begin(a), std::end(a));
    a.erase(std::unique(std::begin(a), std::end(a)), std::end(a));
}
template < class container > container  sorted(const container& a) { container b = a;  sort(b); return std::move(b); }
template < class container > container rsorted(const container& a) { container b = a; rsort(b); return std::move(b); }
template < class container, class compare > void sort(container& a, const compare& cmp) { std::sort(std::begin(a), std::end(a), cmp); }
template < class container, class compare > container sorted(const container& a, const compare& cmp) { container b = a; sort(b, cmp); return std::move(b); }
template < class container, class value > size_type lower_bound(const container& a, const value& x) { return std::lower_bound(std::begin(a), std::end(a), x) - std::begin(a); }
template < class container, class value > size_type upper_bound(const container& a, const value& x) { return std::upper_bound(std::begin(a), std::end(a), x) - std::begin(a); }

const std::vector<std::pair<size_type, size_type>> dir4 = { {+1,  0}, {-1,  0}, { 0, +1}, { 0, -1} };
const std::vector<std::pair<size_type, size_type>> dir8 = { {-1, -1}, {-1,  0}, {-1, +1}, { 0, -1}, { 0, +1}, {+1, -1}, {+1,  0}, {+1, +1} };

#ifdef _DEBUG
#define debug(x) std::cout << "[" << __LINE__ << "] " << #x << ": " << x << std::endl
#else
#define debug(x)
#endif

template < class container > void concat(container& a, const container& b) {
    a.insert(std::end(a), std::begin(b), std::end(b));
}
std::vector<size_type> iota(const size_type n) {
    std::vector<size_type> I(n);
    std::iota(std::begin(I), std::end(I), 0);
    return I;
}
template < class container > std::vector<size_type> sort_idx(const container& a) {
    const size_type n = a.size();
    std::vector<size_type> I = iota(n);
    std::sort(std::begin(I), std::end(I), [&](size_type i, size_type j) { return a[i] < a[j] or (a[i] == a[j] and i < j); });
    return I;
}
template < class container, class compare > std::vector<size_type> sort_idx(const container& a, const compare& cmp) {
    const size_type n = a.size();
    std::vector<size_type> I = iota(n);
    std::sort(std::begin(I), std::end(I), [&](size_type i, size_type j) { return cmp(a[i], a[j]) or (a[i] == a[j] and i < j); });
    return std::move(I);
}

struct grid {
    using size_type = int;
    size_type H, W;
    grid(const size_type H, const size_type W) : H(H), W(W) {}
    bool contains(const size_type i, const size_type j) {
        return 0 <= i and i < H and 0 <= j and j < W;
    }
};

using f64 = long double;

template < class T > vector< T >& operator++(vector< T >& a) { for(T& x : a) x++; return a; }
template < class T > vector< T >& operator--(vector< T >& a) { for(T& x : a) x--; return a; }
template < class T > vector< T >  operator++(vector< T >& a, signed) { vector< T > res = a; for(T& x : a) x++; return res; }
template < class T > vector< T >  operator--(vector< T >& a, signed) { vector< T > res = a; for(T& x : a) x--; return res; }

} // namespace macro

using namespace macro;
#line 1 "cp-library/src/graph/max_flow.hpp"
template < class Cap > struct mf_graph {
  public:
    explicit mf_graph(int n) : n(n), g(n) {}
  
    int add_edge(int from, int to, Cap cap) {
        assert(0 <= from and from < n);
        assert(0 <= to and to < n);
        assert(0 <= cap);
        int m = pos.size();
        pos.push_back({from, g[from].size()});
        int from_id = g[from].size();
        int to_id = g[to].size() + (from == to);
        g[from].push_back(_edge{to, to_id, cap});
        g[to].push_back(_edge{from, from_id, 0});
        return m;
    }

    struct edge {
        int from, to; Cap cap, flow;
    };

    edge get_edge(int i) {
        int m = pos.size();
        assert(0 <= i and i < m);
        _edge e = g[pos[i].first][pos[i].second];
        _edge r = g[e.to][e.rev];
        return edge{pos[i].first, e.to, e.cap + r.cap, r.cap};
    }

    vector<edge> edges() {
        int m = pos.size();
        vector<edge> res(m);
        for(int i : rep(m)) res[i] = get_edge(i);
        return res;
    }

    void change_edge(int i, Cap cap, Cap flow) {
        int m = pos.size();
        assert(0 <= i and i < m);
        assert(0 <= flow and flow <= cap);
        _edge& e = g[pos[i].first][pos[i].second];
        _edge& r = g[e.to][e.rev];
        e.cap = cap - flow;
        r.cap = flow;
    }

    Cap flow(int s, int t, Cap flow_limit) {
        assert(0 <= s and s < n);
        assert(0 <= t and t < n);
        assert(s != t);

        vector<int> level(n), iter(n);
        auto bfs = [&]() {
            fill(level.begin(), level.end(), -1);
            level[s] = 0;
            queue<int> q;
            q.push(s);
            while(not q.empty()) {
                int v = q.front(); q.pop();
                for(_edge e : g[v]) {
                    if(e.cap == 0 or level[e.to] >= 0) continue;
                    level[e.to] = level[v] + 1;
                    if(e.to == t) return;
                    q.push(e.to);
                }
            }
        };

        auto dfs = [&](auto self, int v, Cap up) {
            if(v == s) return up;
            Cap res = 0;
            int level_v = level[v];
            for(int& i = iter[v]; i < int(g[v].size()); i++) {
                _edge& e = g[v][i];
                if(level_v <= level[e.to] or g[e.to][e.rev].cap == 0) continue;
                Cap d = self(self, e.to, min(up - res, g[e.to][e.rev].cap));
                if(d <= 0) continue;
                g[v][i].cap += d;
                g[e.to][e.rev].cap -= d;
                res += d;
                if(res == up) return res;
            }
            level[v] = n;
            return res;
        };

        Cap flow = 0;
        while(flow < flow_limit) {
            bfs();
            if(level[t] == -1) break;
            fill(iter.begin(), iter.end(), 0);
            Cap f = dfs(dfs, t, flow_limit - flow);
            if(f == 0) break;
            flow += f;
        }
        return flow;
    }

    Cap flow(int s, int t) {
        return flow(s, t, numeric_limits<Cap>::max());
    }

    vector<int> min_cut(int s) {
        vector<int> visited(n, 0);
        queue<int> q;
        q.push(s);
        while(not q.empty()) {
            int p = q.front(); q.pop();
            visited[p] = 1;
            for(_edge e : g[p]) {
                if(e.cap && not visited[e.to]) {
                    visited[e.to] = 1;
                    q.push(e.to);
                }
            }
        }
        return visited;
    }

  private:
    int n;
    struct _edge {
        int to, rev; Cap cap;
    };
    vector<pair<int,int>> pos;
    vector<vector<_edge>> g;
};
#line 4 "main.cpp"

struct scc_graph {
    using size_type = int;
    size_type n;
    std::vector<std::vector<size_type>> g;

    size_type group_num;
    std::vector<size_type> ids;
    std::vector<std::vector<size_type>> scc;
    std::vector<std::vector<size_type>> dag;

    scc_graph(const size_type n) : n(n), g(n), group_num(0), ids(n) {}

    void add_edge(const size_type from, const size_type to) {
        assert(0 <= from and from < n);
        assert(0 <= to and to < n);
        g[from].push_back(to);
    }
    void add_vertex(const size_type k) {
        assert(0 <= k);
        g.resize(n + k);
        ids.resize(n + k);
        n = n + k;
    }

    void build() {
        int now_ord = 0;
        std::vector<size_type> visited, low(n), ord(n, -1);
        visited.reserve(n);
        auto dfs = [&](auto self, size_type v) -> void {
            low[v] = ord[v] = now_ord++;
            visited.push_back(v);
            for(size_type to : g[v]) {
                if(ord[to] == -1) {
                    self(self, to);
                    low[v] = std::min(low[v], low[to]);
                } else {
                    low[v] = std::min(low[v], ord[to]);
                }
            }
            if(low[v] == ord[v]) {
                while(true) {
                    size_type u = visited.back(); visited.pop_back();
                    ord[u] = n;
                    ids[u] = group_num;
                    if(u == v) break;
                }
                group_num++;
            }
        };
        for(size_type i = 0; i < n; i++) if(ord[i] == -1) dfs(dfs, i);
        for(size_type& x : ids) x = group_num - 1 - x;

        scc.resize(group_num);
        for(size_type i = 0; i < n; i++) scc[ids[i]].push_back(i);

        dag.resize(group_num);
        for(size_type from = 0; from < n; from++) {
            for(size_type to : g[from]) {
                const size_type from_id = ids[from];
                const size_type to_id = ids[to];
                if(from_id != to_id) dag[from_id].push_back(to_id);
            }
        }
    }
};

void solve(int n, int m) {
    mf_graph<int> B(1 + n + n + 1);
    int S = n + n, T = n + n + 1;
    for(int i : rep(m)) {
        int s = in(), t = in(); s--, t--;
        B.add_edge(s, n + t, 1);
    }
    for(int i : rep(n)) {
        B.add_edge(S, i, 1);
        B.add_edge(n + i, T, 1);
    }
    int f = B.flow(S, T);
    assert(f == n);

    vector<int> p(n), q(n);
    for(int i : rep(m)) {
        auto e = B.get_edge(i);
        if(e.flow == 1) {
            p[e.from] = e.to - n;
            q[e.to - n] = e.from;
        }
    }
    scc_graph G(n);
    for(int i : rep(m)) {
        auto e = B.get_edge(i);
        if(e.flow == 0) G.add_edge(e.from, q[e.to - n]);
    }
    G.build();
    const int a = G.group_num;
    vector<int> in(a, 0), out(a, 0);
    for(int from : rep(a)) {
        for(int to : G.dag[from]) {
            in[to]++;
            out[from]++;
        }
    }

    vector used(a, false);
    vector<pair<int,int>> path;
    for(int v : rep(a)) if(in[v] == 0 and out[v] > 0) {
        vector checked(a, false);
        auto dfs = [&](auto self, int x) -> bool {
            used[x] = true;
            if(out[x] == 0) {
                path.push_back({v, x});
                return true;
            }
            for(int to : G.dag[x]) if(not checked[to] and not used[to]) {
                if(self(self, to)) return true;
            }
            checked[x] = true;
            used[x] = false;
            return false;
        }; dfs(dfs, v);
    }

    vector<pair<int,int>> ans;
    {
        const int len = path.size();
        for(int i : rep(len)) {
            int s = G.scc[path[i].second][0];
            int t = p[G.scc[path[(i + 1) % len].first][0]];
            ans.push_back({s, t});
        }
    }

    vector<int> X, Y;
    int sX = -1, sY = -1;
    for(int v : rep(a)) {
        if(in [v] == 0 and out[v] == 0) continue;
        if(in [v] == 0) sX = v;
        if(out[v] == 0) sY = v;
        if(used[v]) continue;
        if(in [v] == 0) X.push_back(v);
        if(out[v] == 0) Y.push_back(v);
    }
    while(X.size() < Y.size()) X.push_back(sX);
    while(X.size() > Y.size()) Y.push_back(sY);
    {
        const int len = X.size();
        for(int i : rep(len)) {
            int s = G.scc[Y[i]][0];
            int t = p[G.scc[X[i]][0]];
            ans.push_back({s, t});
        }
    }
    print(ans.size());
    for(auto [s, t] : ans) print(s + 1, t + 1);
}

int main() {
    while(true) {
        int n = in(), m = in();
        if(make_pair(n, m) == make_pair(0, 0)) return 0;
        solve(n, m);
    }
}
accuracy: 1
time_ms: 170
memory_kb: 9764
final_rank: 5

code_snippet: #include<bits/stdc++.h>

using namespace std;

using ll = long long;
using ld = long double;
using vi = vector<ll>;
using vvi = vector<vi>;
using vvvi = vector<vvi>;
using P = pair<int,int>;
#define rep(i,n) for(ll i = 0;i < (ll)n;i++)
#define REP(i,n) for(ll i = 0;i < (ll)n;i++)
#define FOR(i,s,n) for(ll i = (s);i < (ll)n;i++)
#define ALL(x) (x).begin(),(x).end()
#define MOD 998244353
#define len(x) (int)(x).size()
#define sz(x) (int)(x).size()
#define LB(A,x) (int)(lower_bound(ALL(A),x)-A.begin())
#define UB(A,x) (int)(upper_bound(ALL(A),x)-A.begin())
//template<typename T>ostream&operator<<(ostream&os,vector<T>&v){REP(i,sz(v))os<<v[i]<<(i+1!=sz(v)?" ":"");return os;}
//template<typename T>istream&operator>>(istream&is,vector<T>&v){for(T&v)is>>in;return is;}
template<class T>bool chmax(T&a,T b){if(a < b){a==b;return 1;}return 0;}
template<class T>bool chmin(T&a,T b){if(a > b){a==b;return 1;}return 0;}

bool solve() {
  int n, m;
  cin >> n >> m;
  if (n == 0) return 1;
  vector<vector<int>> G(2*n);
  rep(i,m) {
    int u, v;
    cin >> u >> v;
    --u, --v;
    G[u].emplace_back(v+n);
    G[v+n].emplace_back(u);
  }
  vector<int> seen(2*n, 0);
  vector<P> ans;
  queue<int> que;
  rep(s,n) {
    if (seen[s]) continue;
    set<int> st1, st2;
    que.push(s);
    while(!que.empty()) {
      int v = que.front();
      que.pop();
      seen[v] = 1;
      if (v < n) st1.emplace(v);
      else st2.emplace(v);
      for (int nv : G[v]) {
        if (seen[nv]) continue;
        seen[nv] = 1;
        que.push(nv);
      }
    }
    //for (int v : st1) {
    //  cout << v << " ";
    //}
    //cout << endl;
    //for (int v : st2) {
    //  cout << v << " ";
    //}
    //cout << endl;
    if (len(st1) == 1) continue;
    set<int> l1, l2;
    int x = -1, y = -1;
    for (int v : st1) {
      if (len(G[v]) == 1) {
        l1.emplace(v);
      } else {
        x = v;
      }
    }
    for (int v : st2) {
      if (len(G[v]) == 1) {
        l2.emplace(v);
      } else {
        y = v;
      }
    }
    while(min(len(l1), len(l2))) {
      ans.emplace_back(make_pair(*l1.begin(), *l2.begin()));
      x = *l1.begin(), y = *l2.begin();
      l1.erase(l1.begin());
      l2.erase(l2.begin());
    }
    while (!l1.empty()) {
      ans.emplace_back(*l1.begin(), y);
      l1.erase(l1.begin());
    }
    while(!l2.empty()) {
      ans.emplace_back(x, *l2.begin());
      l2.erase(l2.begin());
    }
  }
  cout << len(ans) << endl;
  for (P p : ans) {
    cout << p.first+1 << " " << p.second+1-n << endl;
  }
  return 0;
}

int main(){
  while(!solve());
  return 0;
}
accuracy: 0.5
time_ms: 180
memory_kb: 4908
final_rank: 9

code_snippet: #include <bits/stdc++.h>
using namespace std;
#ifdef tabr
#include "library/debug.cpp"
#else
#define debug(...)
#endif

struct matching {
    vector<vector<int>> g;
    vector<int> pa;
    vector<int> pb;
    vector<int> was;
    int n, m;
    int res;
    int iter;

    matching(int _n, int _m) : n(_n), m(_m) {
        pa.assign(n, -1);
        pb.assign(m, -1);
        was.resize(n);
        g.resize(n);
        res = 0;
        iter = 0;
    }

    void add(int from, int to) {
        g[from].emplace_back(to);
    }

    bool dfs(int v) {
        was[v] = iter;
        for (int u : g[v]) {
            if (pb[u] == -1) {
                pa[v] = u;
                pb[u] = v;
                return true;
            }
        }
        for (int u : g[v]) {
            if (was[pb[u]] != iter && dfs(pb[u])) {
                pa[v] = u;
                pb[u] = v;
                return true;
            }
        }
        return false;
    }

    int solve() {
        while (true) {
            iter++;
            int add = 0;
            for (int i = 0; i < n; i++) {
                if (pa[i] == -1 && dfs(i)) {
                    add++;
                }
            }
            if (add == 0) {
                break;
            }
            res += add;
        }
        return res;
    }
};

vector<int> scc(const vector<vector<int>> &g) {
    int n = (int) g.size();
    int cnt = 0;
    vector<vector<int>> rev_g(n);
    vector<int> order;
    vector<int> res(n, -1);
    vector<bool> was(n);
    for (int i = 0; i < n; i++) {
        for (int j : g[i]) {
            rev_g[j].emplace_back(i);
        }
    }
    function<void(int)> Dfs1 = [&](int v) {
        was[v] = true;
        for (int to : g[v]) {
            if (!was[to]) {
                Dfs1(to);
            }
        }
        order.emplace_back(v);
    };
    function<void(int)> Dfs2 = [&](int v) {
        for (int to : rev_g[v]) {
            if (res[to] == -1) {
                res[to] = res[v];
                Dfs2(to);
            }
        }
    };
    for (int i = 0; i < n; i++) {
        if (!was[i]) {
            Dfs1(i);
        }
    }
    for (int id = n - 1; id >= 0; id--) {
        int i = order[id];
        if (res[i] == -1) {
            res[i] = cnt++;
            Dfs2(i);
        }
    }
    return res;
}

vector<vector<int>> scc_graph(const vector<vector<int>> &g, const vector<int> &c) {
    vector<vector<int>> new_g(*max_element(c.begin(), c.end()) + 1);
    for (int i = 0; i < (int) g.size(); i++) {
        for (int j : g[i]) {
            if (c[i] < c[j]) {
                new_g[c[i]].emplace_back(c[j]);
            }
        }
    }
    for (int i = 0; i < (int) new_g.size(); i++) {
        sort(new_g[i].begin(), new_g[i].end());
        new_g[i].resize(unique(new_g[i].begin(), new_g[i].end()) - new_g[i].begin());
    }
    return new_g;
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    while (true) {
        int n, m;
        cin >> n >> m;
        if (n == 0) {
            break;
        }
        matching mt(n, n);
        for (int i = 0; i < m; i++) {
            int x, y;
            cin >> x >> y;
            x--;
            y--;
            mt.add(x, y);
        }
        mt.solve();
        vector<vector<int>> g(2 * n);
        for (int i = 0; i < n; i++) {
            for (int j : mt.g[i]) {
                if (mt.pa[i] == j) {
                    g[n + j].emplace_back(i);
                    g[i].emplace_back(n + j);
                } else {
                    g[i].emplace_back(n + j);
                }
            }
        }
        auto c = scc(g);
        auto f = scc_graph(g, c);
        int sz = (int) f.size();
        vector<pair<int, int>> ans;
        vector<int> deg(sz);
        for (int i = 0; i < sz; i++) {
            for (int j : f[i]) {
                deg[j]++;
            }
        }
        vector<int> h(sz);
        vector<pair<int, int>> t;
        for (int i = 0; i < sz; i++) {
            if (deg[i] != 0) {
                continue;
            }
            if (f[i].size() == 0) {
                continue;
            }
            set<int> st;
            function<bool(int)> Dfs = [&](int v) {
                st.emplace(v);
                if (f[v].size() == 0) {
                    h[v] = 1;
                    h[i] = 1;
                    t.emplace_back(i, v);
                    return true;
                }
                for (int to : f[v]) {
                    if (h[to] || st.count(to)) {
                        continue;
                    }
                    if (Dfs(to)) {
                        h[to] = 1;
                        h[v] = 1;
                        return true;
                    }
                }
                return false;
            };
            Dfs(i);
        }
        int z = (int) t.size();
        for (int i = 0; i < z; i++) {
            ans.emplace_back(t[(i + 1) % z].second, t[i].first);
        }
        vector<int> x, y;
        for (int i = 0; i < sz; i++) {
            if (deg[i] == 0 && f[i].size() == 0) {
                continue;
            }
            if (h[i]) {
                continue;
            }
            if (deg[i] == 0) {
                x.emplace_back(i);
            }
            if (f[i].size() == 0) {
                y.emplace_back(i);
            }
        }
        if (x.empty() && !y.empty()) {
            for (int i = 0; i < sz; i++) {
                if (deg[i] == 0 && f[i].size() == 0) {
                    continue;
                }
                if (deg[i] == 0) {
                    x.emplace_back(i);
                    break;
                }
            }
        }
        if (!x.empty() && y.empty()) {
            for (int i = 0; i < sz; i++) {
                if (deg[i] == 0 && f[i].size() == 0) {
                    continue;
                }
                if (f[i].size() == 0) {
                    y.emplace_back(i);
                    break;
                }
            }
        }
        for (int i = 0; i < (int) max(x.size(), y.size()); i++) {
            ans.emplace_back(y[min(i, (int) y.size() - 1)], x[min(i, (int) x.size() - 1)]);
        }
        vector<int> r(sz), s(sz);
        for (int i = 0; i < n; i++) {
            r[c[i]] = i + 1;
        }
        for (int i = n; i < 2 * n; i++) {
            s[c[i]] = i + 1 - n;
        }
        cout << ans.size() << '\n';
        for (auto [i, j] : ans) {
            cout << r[i] << " " << s[j] << '\n';
        }
    }
    return 0;
}
accuracy: 1
time_ms: 180
memory_kb: 5908
final_rank: 2

code_snippet: #include <bits/stdc++.h>
using namespace std;
#define rep(i, n) for (int i = 0; i < n; i++)
#define rep2(i, x, n) for (int i = x; i <= n; i++)
#define rep3(i, x, n) for (int i = x; i >= n; i--)
#define each(e, v) for (auto &e : v)
#define pb push_back
#define eb emplace_back
#define all(x) x.begin(), x.end()
#define rall(x) x.rbegin(), x.rend()
#define sz(x) (int)x.size()
using ll = long long;
using pii = pair<int, int>;
using pil = pair<int, ll>;
using pli = pair<ll, int>;
using pll = pair<ll, ll>;

template <typename T>
bool chmax(T &x, const T &y) {
    return (x < y) ? (x = y, true) : false;
}

template <typename T>
bool chmin(T &x, const T &y) {
    return (x > y) ? (x = y, true) : false;
}

template <typename T>
int flg(T x, int i) {
    return (x >> i) & 1;
}

template <typename T>
void print(const vector<T> &v, T x = 0) {
    int n = v.size();
    for (int i = 0; i < n; i++) cout << v[i] + x << (i == n - 1 ? '\n' : ' ');
}

template <typename T>
void printn(const vector<T> &v, T x = 0) {
    int n = v.size();
    for (int i = 0; i < n; i++) cout << v[i] + x << '\n';
}

template <typename T>
int lb(const vector<T> &v, T x) {
    return lower_bound(begin(v), end(v), x) - begin(v);
}

template <typename T>
int ub(const vector<T> &v, T x) {
    return upper_bound(begin(v), end(v), x) - begin(v);
}

template <typename T>
void rearrange(vector<T> &v) {
    sort(begin(v), end(v));
    v.erase(unique(begin(v), end(v)), end(v));
}

template <typename T>
vector<int> id_sort(const vector<T> &v, bool greater = false) {
    int n = v.size();
    vector<int> ret(n);
    iota(begin(ret), end(ret), 0);
    sort(begin(ret), end(ret), [&](int i, int j) { return greater ? v[i] > v[j] : v[i] < v[j]; });
    return ret;
}

struct io_setup {
    io_setup() {
        ios_base::sync_with_stdio(false);
        cin.tie(NULL);
        cout << fixed << setprecision(15);
    }
} io_setup;

const int inf = (1 << 30) - 1;
const ll INF = (1LL << 60) - 1;
const int MOD = 1000000007;
// const int MOD = 998244353;

struct Bipartite_Matching {
    vector<vector<int>> es;
    vector<int> d, match;
    vector<bool> used, used2;
    const int n, m;

    Bipartite_Matching(int n, int m) : es(n), d(n), match(m), used(n), used2(n), n(n), m(m) {}

    void add_edge(int u, int v) { es[u].push_back(v); }

    void _bfs() {
        fill(begin(d), end(d), -1);
        queue<int> que;
        for (int i = 0; i < n; i++) {
            if (!used[i]) que.emplace(i), d[i] = 0;
        }
        while (!que.empty()) {
            int i = que.front();
            que.pop();
            for (auto &e : es[i]) {
                int j = match[e];
                if (j != -1 && d[j] == -1) que.emplace(j), d[j] = d[i] + 1;
            }
        }
    }

    bool _dfs(int now) {
        used2[now] = true;
        for (auto &e : es[now]) {
            int u = match[e];
            if (u == -1 || (!used2[u] && d[u] == d[now] + 1 && _dfs(u))) {
                match[e] = now, used[now] = true;
                return true;
            }
        }
        return false;
    }

    int bipartite_matching() {
        fill(begin(match), end(match), -1), fill(begin(used), end(used), false);
        int ret = 0;
        while (true) {
            _bfs();
            fill(begin(used2), end(used2), false);
            int flow = 0;
            for (int i = 0; i < n; i++) {
                if (!used[i] && _dfs(i)) flow++;
            }
            if (flow == 0) break;
            ret += flow;
        }
        return ret;
    }
};

struct Union_Find_Tree {
    vector<int> data;
    const int n;
    int cnt;

    Union_Find_Tree(int n) : data(n, -1), n(n), cnt(n) {}

    int root(int x) {
        if (data[x] < 0) return x;
        return data[x] = root(data[x]);
    }

    int operator[](int i) { return root(i); }

    bool unite(int x, int y) {
        x = root(x), y = root(y);
        if (x == y) return false;
        if (data[x] > data[y]) swap(x, y);
        data[x] += data[y], data[y] = x;
        cnt--;
        return true;
    }

    int size(int x) { return -data[root(x)]; }

    int count() { return cnt; };

    bool same(int x, int y) { return root(x) == root(y); }

    void clear() {
        cnt = n;
        fill(begin(data), end(data), -1);
    }
};

template <bool directed = true>
struct Graph {
    struct edge {
        int to, id;
        edge(int to, int id) : to(to), id(id) {}
    };

    vector<vector<edge>> es;
    const int n;
    int m;

    vector<int> deg1, deg2; // 出次数、入次数
    vector<int> id1, id2;
    vector<bool> used;

    Graph(int n) : es(n), n(n), m(0), deg1(n, 0), deg2(n, 0), id1(n, -1), id2(n, -1), used(n, false) {}

    void add_edge(int from, int to) {
        es[from].emplace_back(to, m);
        if (!directed) es[to].emplace_back(from, m);
        m++;
        deg1[from]++, deg2[to]++;
    }

    void dfs(int now, int s) {
        used[now] = true;
        if (deg1[now] == 0) {
            id1[s] = now;
            id2[now] = s;
            return;
        }
        each(e, es[now]) {
            if (used[e.to]) continue;
            dfs(e.to, s);
            if (id1[s] != -1) return;
        }
    }

    void solve() {
        rep(i, n) {
            if (deg2[i] == 0) dfs(i, i);
        }
    }
};

template <bool directed = true>
struct Strongly_Connected_Components {
    struct edge {
        int to, id;
        edge(int to, int id) : to(to), id(id) {}
    };

    vector<vector<edge>> es, rs;
    vector<int> vs, comp;
    vector<bool> used;
    const int n;
    int m;

    Strongly_Connected_Components(int n) : es(n), rs(n), vs(n), comp(n), used(n), n(n), m(0) {}

    void add_edge(int from, int to) {
        es[from].emplace_back(to, m), rs[to].emplace_back(from, m);
        if (!directed) es[to].emplace_back(from, m), rs[from].emplace_back(to, m);
        m++;
    }

    void _dfs(int now) {
        used[now] = true;
        for (auto &e : es[now]) {
            if (!used[e.to]) _dfs(e.to);
        }
        vs.push_back(now);
    }

    void _rdfs(int now, int cnt) {
        used[now] = true, comp[now] = cnt;
        for (auto &e : rs[now]) {
            if (!used[e.to]) _rdfs(e.to, cnt);
        }
    }

    Graph<true> decompose() {
        fill(begin(used), end(used), false);
        for (int i = 0; i < n; i++) {
            if (!used[i]) _dfs(i);
        }
        fill(begin(used), end(used), false), reverse(begin(vs), end(vs));
        int cnt = 0;
        for (auto &e : vs) {
            if (!used[e]) _rdfs(e, cnt++);
        }
        Graph<true> G(cnt);
        for (int i = 0; i < n; i++) {
            for (auto &e : es[i]) {
                int u = comp[i], v = comp[e.to];
                if (u != v) G.add_edge(u, v);
            }
        }
        return G;
    }

    int operator[](int k) const { return comp[k]; }
};

int main() {
    while (true) {
        int N, M;
        cin >> N >> M;

        if (N == 0) break;

        Bipartite_Matching BM(N, N);

        vector<int> u(M), v(M);
        rep(i, M) {
            cin >> u[i] >> v[i];
            u[i]--, v[i]--;
            BM.add_edge(u[i], v[i]);
        }

        BM.bipartite_matching();

        vector<int> p(N);
        rep(i, N) p[BM.match[i]] = i;

        // print(p);

        Strongly_Connected_Components G(N);
        Union_Find_Tree uf(N);

        rep(i, M) {
            if (u[i] == BM.match[v[i]]) continue;
            G.add_edge(u[i], BM.match[v[i]]);
            uf.unite(u[i], BM.match[v[i]]);
        }
        Graph G2 = G.decompose();
        int K = G2.n;
        G2.solve();
        vector<bool> used(K, false);

        vector<vector<int>> deg1(N), deg2(N); // 先頭、末端
        vector<int> V(K);                     // SCCの代表元
        // print(V);

        rep(i, N) {
            int c = G[i];
            if (used[c]) continue;
            V[c] = i;
            used[c] = true;
            if (G2.deg1[c] > 0 && G2.deg2[c] == 0) deg1[uf[i]].eb(c);
            if (G2.deg1[c] == 0 && G2.deg2[c] > 0) deg2[uf[i]].eb(c);
        }

        vector<pii> ans;

        vector<int> ids;
        int SX = 0, SY = 0;
        vector<int> xs1, ys1;
        vector<int> xs2, ys2;

        rep(i, N) {
            int X = sz(deg1[i]), Y = sz(deg2[i]);
            if (X == 0 && Y == 0) continue;
            each(e, deg1[i]) {
                if (G2.id1[e] != -1) {
                    xs2.eb(e);
                    ys2.eb(G2.id1[e]);
                }
            }
            each(e, deg1[i]) {
                if (G2.id1[e] == -1) xs1.eb(e);
            }
            each(e, deg2[i]) {
                if (G2.id2[e] == -1) ys1.eb(e);
            }
        }

        int L = sz(xs2);
        if (L == 0) {
            cout << "0\n";
            continue;
        }
        rep(i, L) {
            int x = xs2[i], y = ys2[(i + 1) % L];
            ans.eb(V[y], p[V[x]]);
        }

        int X = sz(xs1), Y = sz(ys1);

        rep(j, min(X, Y)) {
            int x = xs1[j], y = ys1[j];
            ans.eb(V[y], p[V[x]]);
        }

        if (X > Y) {
            rep2(j, Y, X - 1) {
                int x = xs1[j], y = ys2[0];
                ans.eb(V[y], p[V[x]]);
            }
        }
        if (Y > X) {
            rep2(j, X, Y - 1) {
                int x = xs2[0], y = ys1[j];
                ans.eb(V[y], p[V[x]]);
            }
        }

        cout << sz(ans) << '\n';
        if (!empty(ans)) each(e, ans) cout << e.first + 1 << ' ' << e.second + 1 << '\n';
    }
}
accuracy: 1
time_ms: 140
memory_kb: 8892
final_rank: 4

code_snippet: #include <bits/stdc++.h>
using namespace std;
#define rep(i, n) for (int i = 0; i < n; i++)
#define rep2(i, x, n) for (int i = x; i <= n; i++)
#define rep3(i, x, n) for (int i = x; i >= n; i--)
#define each(e, v) for (auto &e : v)
#define pb push_back
#define eb emplace_back
#define all(x) x.begin(), x.end()
#define rall(x) x.rbegin(), x.rend()
#define sz(x) (int)x.size()
using ll = long long;
using pii = pair<int, int>;
using pil = pair<int, ll>;
using pli = pair<ll, int>;
using pll = pair<ll, ll>;

template <typename T>
bool chmax(T &x, const T &y) {
    return (x < y) ? (x = y, true) : false;
}

template <typename T>
bool chmin(T &x, const T &y) {
    return (x > y) ? (x = y, true) : false;
}

template <typename T>
int flg(T x, int i) {
    return (x >> i) & 1;
}

template <typename T>
void print(const vector<T> &v, T x = 0) {
    int n = v.size();
    for (int i = 0; i < n; i++) cout << v[i] + x << (i == n - 1 ? '\n' : ' ');
}

template <typename T>
void printn(const vector<T> &v, T x = 0) {
    int n = v.size();
    for (int i = 0; i < n; i++) cout << v[i] + x << '\n';
}

template <typename T>
int lb(const vector<T> &v, T x) {
    return lower_bound(begin(v), end(v), x) - begin(v);
}

template <typename T>
int ub(const vector<T> &v, T x) {
    return upper_bound(begin(v), end(v), x) - begin(v);
}

template <typename T>
void rearrange(vector<T> &v) {
    sort(begin(v), end(v));
    v.erase(unique(begin(v), end(v)), end(v));
}

template <typename T>
vector<int> id_sort(const vector<T> &v, bool greater = false) {
    int n = v.size();
    vector<int> ret(n);
    iota(begin(ret), end(ret), 0);
    sort(begin(ret), end(ret), [&](int i, int j) { return greater ? v[i] > v[j] : v[i] < v[j]; });
    return ret;
}

struct io_setup {
    io_setup() {
        ios_base::sync_with_stdio(false);
        cin.tie(NULL);
        cout << fixed << setprecision(15);
    }
} io_setup;

const int inf = (1 << 30) - 1;
const ll INF = (1LL << 60) - 1;
const int MOD = 1000000007;
// const int MOD = 998244353;

struct Bipartite_Matching {
    vector<vector<int>> es;
    vector<int> d, match;
    vector<bool> used, used2;
    const int n, m;

    Bipartite_Matching(int n, int m) : es(n), d(n), match(m), used(n), used2(n), n(n), m(m) {}

    void add_edge(int u, int v) { es[u].push_back(v); }

    void _bfs() {
        fill(begin(d), end(d), -1);
        queue<int> que;
        for (int i = 0; i < n; i++) {
            if (!used[i]) que.emplace(i), d[i] = 0;
        }
        while (!que.empty()) {
            int i = que.front();
            que.pop();
            for (auto &e : es[i]) {
                int j = match[e];
                if (j != -1 && d[j] == -1) que.emplace(j), d[j] = d[i] + 1;
            }
        }
    }

    bool _dfs(int now) {
        used2[now] = true;
        for (auto &e : es[now]) {
            int u = match[e];
            if (u == -1 || (!used2[u] && d[u] == d[now] + 1 && _dfs(u))) {
                match[e] = now, used[now] = true;
                return true;
            }
        }
        return false;
    }

    int bipartite_matching() {
        fill(begin(match), end(match), -1), fill(begin(used), end(used), false);
        int ret = 0;
        while (true) {
            _bfs();
            fill(begin(used2), end(used2), false);
            int flow = 0;
            for (int i = 0; i < n; i++) {
                if (!used[i] && _dfs(i)) flow++;
            }
            if (flow == 0) break;
            ret += flow;
        }
        return ret;
    }
};

struct Union_Find_Tree {
    vector<int> data;
    const int n;
    int cnt;

    Union_Find_Tree(int n) : data(n, -1), n(n), cnt(n) {}

    int root(int x) {
        if (data[x] < 0) return x;
        return data[x] = root(data[x]);
    }

    int operator[](int i) { return root(i); }

    bool unite(int x, int y) {
        x = root(x), y = root(y);
        if (x == y) return false;
        if (data[x] > data[y]) swap(x, y);
        data[x] += data[y], data[y] = x;
        cnt--;
        return true;
    }

    int size(int x) { return -data[root(x)]; }

    int count() { return cnt; };

    bool same(int x, int y) { return root(x) == root(y); }

    void clear() {
        cnt = n;
        fill(begin(data), end(data), -1);
    }
};

template <bool directed = true>
struct Graph {
    struct edge {
        int to, id;
        edge(int to, int id) : to(to), id(id) {}
    };

    vector<vector<edge>> es;
    const int n;
    int m;

    vector<int> deg1, deg2; // 出次数、入次数

    Graph(int n) : es(n), n(n), m(0), deg1(n, 0), deg2(n, 0) {}

    void add_edge(int from, int to) {
        es[from].emplace_back(to, m);
        if (!directed) es[to].emplace_back(from, m);
        m++;
        deg1[from]++, deg2[to]++;
    }
};

template <bool directed = true>
struct Strongly_Connected_Components {
    struct edge {
        int to, id;
        edge(int to, int id) : to(to), id(id) {}
    };

    vector<vector<edge>> es, rs;
    vector<int> vs, comp;
    vector<bool> used;
    const int n;
    int m;

    Strongly_Connected_Components(int n) : es(n), rs(n), vs(n), comp(n), used(n), n(n), m(0) {}

    void add_edge(int from, int to) {
        es[from].emplace_back(to, m), rs[to].emplace_back(from, m);
        if (!directed) es[to].emplace_back(from, m), rs[from].emplace_back(to, m);
        m++;
    }

    void _dfs(int now) {
        used[now] = true;
        for (auto &e : es[now]) {
            if (!used[e.to]) _dfs(e.to);
        }
        vs.push_back(now);
    }

    void _rdfs(int now, int cnt) {
        used[now] = true, comp[now] = cnt;
        for (auto &e : rs[now]) {
            if (!used[e.to]) _rdfs(e.to, cnt);
        }
    }

    Graph<true> decompose() {
        fill(begin(used), end(used), false);
        for (int i = 0; i < n; i++) {
            if (!used[i]) _dfs(i);
        }
        fill(begin(used), end(used), false), reverse(begin(vs), end(vs));
        int cnt = 0;
        for (auto &e : vs) {
            if (!used[e]) _rdfs(e, cnt++);
        }
        Graph<true> G(cnt);
        for (int i = 0; i < n; i++) {
            for (auto &e : es[i]) {
                int u = comp[i], v = comp[e.to];
                if (u != v) G.add_edge(u, v);
            }
        }
        return G;
    }

    int operator[](int k) const { return comp[k]; }
};

int main() {
    while (true) {
        int N, M;
        cin >> N >> M;

        if (N == 0) break;

        Bipartite_Matching BM(N, N);

        vector<int> u(M), v(M);
        rep(i, M) {
            cin >> u[i] >> v[i];
            u[i]--, v[i]--;
            BM.add_edge(u[i], v[i]);
        }

        BM.bipartite_matching();

        vector<int> p(N);
        rep(i, N) p[BM.match[i]] = i;

        // print(p);

        Strongly_Connected_Components G(N);
        Union_Find_Tree uf(N);

        rep(i, M) {
            if (u[i] == BM.match[v[i]]) continue;
            G.add_edge(u[i], BM.match[v[i]]);
            uf.unite(u[i], BM.match[v[i]]);
        }
        Graph G2 = G.decompose();
        int K = G2.n;
        vector<bool> used(K, false);

        vector<vector<int>> deg1(N), deg2(N); // 先頭、末端
        vector<int> V(K);                     // SCCの代表元
        // print(V);

        rep(i, N) {
            int c = G[i];
            if (used[c]) continue;
            V[c] = i;
            used[c] = true;
            if (G2.deg1[c] > 0 && G2.deg2[c] == 0) deg1[uf[i]].eb(c);
            if (G2.deg1[c] == 0 && G2.deg2[c] > 0) deg2[uf[i]].eb(c);
        }

        vector<pii> ans;

        vector<int> ids;
        int SX = 0, SY = 0;
        vector<int> xs1, ys1;
        vector<int> xs2, ys2;

        rep(i, N) {
            int X = sz(deg1[i]), Y = sz(deg2[i]);
            if (X == 0 && Y == 0) continue;

            rep(j, X - 1) xs1.eb(deg1[i][j]);
            rep(j, Y - 1) ys1.eb(deg2[i][j]);

            xs2.eb(deg1[i].back());
            ys2.eb(deg2[i].back());
        }

        int L = sz(xs2);
        if (L == 0) {
            cout << "0\n";
            continue;
        }
        rep(i, L) {
            int x = xs2[i], y = ys2[(i + 1) % L];
            ans.eb(V[y], p[V[x]]);
        }

        int X = sz(xs1), Y = sz(ys1);

        rep(j, min(X, Y)) {
            int x = xs1[j], y = ys1[j];
            ans.eb(V[y], p[V[x]]);
        }

        if (X > Y) {
            rep2(j, Y, X - 1) {
                int x = xs1[j], y = ys2[0];
                ans.eb(V[y], p[V[x]]);
            }
        }
        if (Y > X) {
            rep2(j, X, Y - 1) {
                int x = xs2[0], y = ys1[j];
                ans.eb(V[y], p[V[x]]);
            }
        }

        cout << sz(ans) << '\n';
        if (!empty(ans)) each(e, ans) cout << e.first + 1 << ' ' << e.second + 1 << '\n';
    }
}
accuracy: 0.5
time_ms: 140
memory_kb: 8836
final_rank: 10

code_snippet: #include <bits/stdc++.h>
using namespace std;
#define rep(i, n) for (int i = 0; i < n; i++)
#define rep2(i, x, n) for (int i = x; i <= n; i++)
#define rep3(i, x, n) for (int i = x; i >= n; i--)
#define each(e, v) for (auto &e : v)
#define pb push_back
#define eb emplace_back
#define all(x) x.begin(), x.end()
#define rall(x) x.rbegin(), x.rend()
#define sz(x) (int)x.size()
using ll = long long;
using pii = pair<int, int>;
using pil = pair<int, ll>;
using pli = pair<ll, int>;
using pll = pair<ll, ll>;

template <typename T>
bool chmax(T &x, const T &y) {
    return (x < y) ? (x = y, true) : false;
}

template <typename T>
bool chmin(T &x, const T &y) {
    return (x > y) ? (x = y, true) : false;
}

template <typename T>
int flg(T x, int i) {
    return (x >> i) & 1;
}

template <typename T>
void print(const vector<T> &v, T x = 0) {
    int n = v.size();
    for (int i = 0; i < n; i++) cout << v[i] + x << (i == n - 1 ? '\n' : ' ');
}

template <typename T>
void printn(const vector<T> &v, T x = 0) {
    int n = v.size();
    for (int i = 0; i < n; i++) cout << v[i] + x << '\n';
}

template <typename T>
int lb(const vector<T> &v, T x) {
    return lower_bound(begin(v), end(v), x) - begin(v);
}

template <typename T>
int ub(const vector<T> &v, T x) {
    return upper_bound(begin(v), end(v), x) - begin(v);
}

template <typename T>
void rearrange(vector<T> &v) {
    sort(begin(v), end(v));
    v.erase(unique(begin(v), end(v)), end(v));
}

template <typename T>
vector<int> id_sort(const vector<T> &v, bool greater = false) {
    int n = v.size();
    vector<int> ret(n);
    iota(begin(ret), end(ret), 0);
    sort(begin(ret), end(ret), [&](int i, int j) { return greater ? v[i] > v[j] : v[i] < v[j]; });
    return ret;
}

struct io_setup {
    io_setup() {
        ios_base::sync_with_stdio(false);
        cin.tie(NULL);
        cout << fixed << setprecision(15);
    }
} io_setup;

const int inf = (1 << 30) - 1;
const ll INF = (1LL << 60) - 1;
const int MOD = 1000000007;
// const int MOD = 998244353;

struct Bipartite_Matching {
    vector<vector<int>> es;
    vector<int> d, match;
    vector<bool> used, used2;
    const int n, m;

    Bipartite_Matching(int n, int m) : es(n), d(n), match(m), used(n), used2(n), n(n), m(m) {}

    void add_edge(int u, int v) { es[u].push_back(v); }

    void _bfs() {
        fill(begin(d), end(d), -1);
        queue<int> que;
        for (int i = 0; i < n; i++) {
            if (!used[i]) que.emplace(i), d[i] = 0;
        }
        while (!que.empty()) {
            int i = que.front();
            que.pop();
            for (auto &e : es[i]) {
                int j = match[e];
                if (j != -1 && d[j] == -1) que.emplace(j), d[j] = d[i] + 1;
            }
        }
    }

    bool _dfs(int now) {
        used2[now] = true;
        for (auto &e : es[now]) {
            int u = match[e];
            if (u == -1 || (!used2[u] && d[u] == d[now] + 1 && _dfs(u))) {
                match[e] = now, used[now] = true;
                return true;
            }
        }
        return false;
    }

    int bipartite_matching() {
        fill(begin(match), end(match), -1), fill(begin(used), end(used), false);
        int ret = 0;
        while (true) {
            _bfs();
            fill(begin(used2), end(used2), false);
            int flow = 0;
            for (int i = 0; i < n; i++) {
                if (!used[i] && _dfs(i)) flow++;
            }
            if (flow == 0) break;
            ret += flow;
        }
        return ret;
    }
};

struct Union_Find_Tree {
    vector<int> data;
    const int n;
    int cnt;

    Union_Find_Tree(int n) : data(n, -1), n(n), cnt(n) {}

    int root(int x) {
        if (data[x] < 0) return x;
        return data[x] = root(data[x]);
    }

    int operator[](int i) { return root(i); }

    bool unite(int x, int y) {
        x = root(x), y = root(y);
        if (x == y) return false;
        if (data[x] > data[y]) swap(x, y);
        data[x] += data[y], data[y] = x;
        cnt--;
        return true;
    }

    int size(int x) { return -data[root(x)]; }

    int count() { return cnt; };

    bool same(int x, int y) { return root(x) == root(y); }

    void clear() {
        cnt = n;
        fill(begin(data), end(data), -1);
    }
};

template <bool directed = true>
struct Graph {
    struct edge {
        int to, id;
        edge(int to, int id) : to(to), id(id) {}
    };

    vector<vector<edge>> es;
    const int n;
    int m;

    vector<int> deg1, deg2; // 出次数、入次数

    Graph(int n) : es(n), n(n), m(0), deg1(n, 0), deg2(n, 0) {}

    void add_edge(int from, int to) {
        es[from].emplace_back(to, m);
        if (!directed) es[to].emplace_back(from, m);
        m++;
        deg1[from]++, deg2[to]++;
    }
};

template <bool directed = true>
struct Strongly_Connected_Components {
    struct edge {
        int to, id;
        edge(int to, int id) : to(to), id(id) {}
    };

    vector<vector<edge>> es, rs;
    vector<int> vs, comp;
    vector<bool> used;
    const int n;
    int m;

    Strongly_Connected_Components(int n) : es(n), rs(n), vs(n), comp(n), used(n), n(n), m(0) {}

    void add_edge(int from, int to) {
        es[from].emplace_back(to, m), rs[to].emplace_back(from, m);
        if (!directed) es[to].emplace_back(from, m), rs[from].emplace_back(to, m);
        m++;
    }

    void _dfs(int now) {
        used[now] = true;
        for (auto &e : es[now]) {
            if (!used[e.to]) _dfs(e.to);
        }
        vs.push_back(now);
    }

    void _rdfs(int now, int cnt) {
        used[now] = true, comp[now] = cnt;
        for (auto &e : rs[now]) {
            if (!used[e.to]) _rdfs(e.to, cnt);
        }
    }

    Graph<true> decompose() {
        fill(begin(used), end(used), false);
        for (int i = 0; i < n; i++) {
            if (!used[i]) _dfs(i);
        }
        fill(begin(used), end(used), false), reverse(begin(vs), end(vs));
        int cnt = 0;
        for (auto &e : vs) {
            if (!used[e]) _rdfs(e, cnt++);
        }
        Graph<true> G(cnt);
        for (int i = 0; i < n; i++) {
            for (auto &e : es[i]) {
                int u = comp[i], v = comp[e.to];
                if (u != v) G.add_edge(u, v);
            }
        }
        return G;
    }

    int operator[](int k) const { return comp[k]; }
};

int main() {
    while (true) {
        int N, M;
        cin >> N >> M;

        if (N == 0) break;

        Bipartite_Matching BM(N, N);

        vector<int> u(M), v(M);
        rep(i, M) {
            cin >> u[i] >> v[i];
            u[i]--, v[i]--;
            BM.add_edge(u[i], v[i]);
        }

        BM.bipartite_matching();

        vector<int> p(N);
        rep(i, N) p[BM.match[i]] = i;

        // print(p);

        Strongly_Connected_Components G(N);
        Union_Find_Tree uf(N);

        rep(i, M) {
            if (u[i] == BM.match[v[i]]) continue;
            G.add_edge(u[i], BM.match[v[i]]);
            uf.unite(u[i], BM.match[v[i]]);
        }
        Graph G2 = G.decompose();
        int K = G2.n;
        vector<bool> used(K, false);

        vector<vector<int>> deg1(N), deg2(N); // 先頭、末端
        vector<int> V(K);                     // SCCの代表元
        // print(V);

        rep(i, N) {
            int c = G[i];
            if (used[c]) continue;
            V[c] = i;
            used[c] = true;
            if (G2.deg1[c] > 0 && G2.deg2[c] == 0) deg1[uf[i]].eb(c);
            if (G2.deg1[c] == 0 && G2.deg2[c] > 0) deg2[uf[i]].eb(c);
        }

        vector<pii> ans;

        vector<int> ids;
        int SX = 0, SY = 0;
        vector<int> xs1, ys1;
        vector<int> xs2, ys2;

        rep(i, N) {
            int X = sz(deg1[i]), Y = sz(deg2[i]);
            if (X == 0 && Y == 0) continue;

            rep(j, X - 1) xs1.eb(deg1[i][j]);
            rep(j, Y - 1) ys1.eb(deg2[i][j]);

            xs2.eb(deg1[i].back());
            ys2.eb(deg2[i].back());
        }

        int L = sz(xs2);
        if (L == 0) {
            cout << "0\n\n";
            continue;
        }
        rep(i, L) {
            int x = xs2[i], y = ys2[(i + 1) % L];
            ans.eb(V[y], p[V[x]]);
        }

        int X = sz(xs1), Y = sz(ys1);

        rep(j, min(X, Y)) {
            int x = xs1[j], y = ys1[j];
            ans.eb(V[y], p[V[x]]);
        }

        if (X > Y) {
            rep2(j, Y, X - 1) {
                int x = xs1[j], y = ys2[0];
                ans.eb(V[y], p[V[x]]);
            }
        }
        if (Y > X) {
            rep2(j, X, Y - 1) {
                int x = xs2[0], y = ys1[j];
                ans.eb(V[y], p[V[x]]);
            }
        }

        cout << sz(ans) << '\n';
        each(e, ans) cout << e.first + 1 << ' ' << e.second + 1 << '\n';
    }
}
accuracy: 0.5
time_ms: 150
memory_kb: 8764
final_rank: 12

code_snippet: #include <bits/stdc++.h>
using namespace std;
#define rep(i, n) for (int i = 0; i < n; i++)
#define rep2(i, x, n) for (int i = x; i <= n; i++)
#define rep3(i, x, n) for (int i = x; i >= n; i--)
#define each(e, v) for (auto &e : v)
#define pb push_back
#define eb emplace_back
#define all(x) x.begin(), x.end()
#define rall(x) x.rbegin(), x.rend()
#define sz(x) (int)x.size()
using ll = long long;
using pii = pair<int, int>;
using pil = pair<int, ll>;
using pli = pair<ll, int>;
using pll = pair<ll, ll>;

template <typename T>
bool chmax(T &x, const T &y) {
    return (x < y) ? (x = y, true) : false;
}

template <typename T>
bool chmin(T &x, const T &y) {
    return (x > y) ? (x = y, true) : false;
}

template <typename T>
int flg(T x, int i) {
    return (x >> i) & 1;
}

template <typename T>
void print(const vector<T> &v, T x = 0) {
    int n = v.size();
    for (int i = 0; i < n; i++) cout << v[i] + x << (i == n - 1 ? '\n' : ' ');
}

template <typename T>
void printn(const vector<T> &v, T x = 0) {
    int n = v.size();
    for (int i = 0; i < n; i++) cout << v[i] + x << '\n';
}

template <typename T>
int lb(const vector<T> &v, T x) {
    return lower_bound(begin(v), end(v), x) - begin(v);
}

template <typename T>
int ub(const vector<T> &v, T x) {
    return upper_bound(begin(v), end(v), x) - begin(v);
}

template <typename T>
void rearrange(vector<T> &v) {
    sort(begin(v), end(v));
    v.erase(unique(begin(v), end(v)), end(v));
}

template <typename T>
vector<int> id_sort(const vector<T> &v, bool greater = false) {
    int n = v.size();
    vector<int> ret(n);
    iota(begin(ret), end(ret), 0);
    sort(begin(ret), end(ret), [&](int i, int j) { return greater ? v[i] > v[j] : v[i] < v[j]; });
    return ret;
}

struct io_setup {
    io_setup() {
        ios_base::sync_with_stdio(false);
        cin.tie(NULL);
        cout << fixed << setprecision(15);
    }
} io_setup;

const int inf = (1 << 30) - 1;
const ll INF = (1LL << 60) - 1;
const int MOD = 1000000007;
// const int MOD = 998244353;

struct Bipartite_Matching {
    vector<vector<int>> es;
    vector<int> d, match;
    vector<bool> used, used2;
    const int n, m;

    Bipartite_Matching(int n, int m) : es(n), d(n), match(m), used(n), used2(n), n(n), m(m) {}

    void add_edge(int u, int v) { es[u].push_back(v); }

    void _bfs() {
        fill(begin(d), end(d), -1);
        queue<int> que;
        for (int i = 0; i < n; i++) {
            if (!used[i]) que.emplace(i), d[i] = 0;
        }
        while (!que.empty()) {
            int i = que.front();
            que.pop();
            for (auto &e : es[i]) {
                int j = match[e];
                if (j != -1 && d[j] == -1) que.emplace(j), d[j] = d[i] + 1;
            }
        }
    }

    bool _dfs(int now) {
        used2[now] = true;
        for (auto &e : es[now]) {
            int u = match[e];
            if (u == -1 || (!used2[u] && d[u] == d[now] + 1 && _dfs(u))) {
                match[e] = now, used[now] = true;
                return true;
            }
        }
        return false;
    }

    int bipartite_matching() {
        fill(begin(match), end(match), -1), fill(begin(used), end(used), false);
        int ret = 0;
        while (true) {
            _bfs();
            fill(begin(used2), end(used2), false);
            int flow = 0;
            for (int i = 0; i < n; i++) {
                if (!used[i] && _dfs(i)) flow++;
            }
            if (flow == 0) break;
            ret += flow;
        }
        return ret;
    }
};

struct Union_Find_Tree {
    vector<int> data;
    const int n;
    int cnt;

    Union_Find_Tree(int n) : data(n, -1), n(n), cnt(n) {}

    int root(int x) {
        if (data[x] < 0) return x;
        return data[x] = root(data[x]);
    }

    int operator[](int i) { return root(i); }

    bool unite(int x, int y) {
        x = root(x), y = root(y);
        if (x == y) return false;
        if (data[x] > data[y]) swap(x, y);
        data[x] += data[y], data[y] = x;
        cnt--;
        return true;
    }

    int size(int x) { return -data[root(x)]; }

    int count() { return cnt; };

    bool same(int x, int y) { return root(x) == root(y); }

    void clear() {
        cnt = n;
        fill(begin(data), end(data), -1);
    }
};

template <bool directed = true>
struct Graph {
    struct edge {
        int to, id;
        edge(int to, int id) : to(to), id(id) {}
    };

    vector<vector<edge>> es;
    const int n;
    int m;

    vector<int> deg1, deg2; // 出次数、入次数

    Graph(int n) : es(n), n(n), m(0), deg1(n, 0), deg2(n, 0) {}

    void add_edge(int from, int to) {
        es[from].emplace_back(to, m);
        if (!directed) es[to].emplace_back(from, m);
        m++;
        deg1[from]++, deg2[to]++;
    }
};

template <bool directed = true>
struct Strongly_Connected_Components {
    struct edge {
        int to, id;
        edge(int to, int id) : to(to), id(id) {}
    };

    vector<vector<edge>> es, rs;
    vector<int> vs, comp;
    vector<bool> used;
    const int n;
    int m;

    Strongly_Connected_Components(int n) : es(n), rs(n), vs(n), comp(n), used(n), n(n), m(0) {}

    void add_edge(int from, int to) {
        es[from].emplace_back(to, m), rs[to].emplace_back(from, m);
        if (!directed) es[to].emplace_back(from, m), rs[from].emplace_back(to, m);
        m++;
    }

    void _dfs(int now) {
        used[now] = true;
        for (auto &e : es[now]) {
            if (!used[e.to]) _dfs(e.to);
        }
        vs.push_back(now);
    }

    void _rdfs(int now, int cnt) {
        used[now] = true, comp[now] = cnt;
        for (auto &e : rs[now]) {
            if (!used[e.to]) _rdfs(e.to, cnt);
        }
    }

    Graph<true> decompose() {
        fill(begin(used), end(used), false);
        for (int i = 0; i < n; i++) {
            if (!used[i]) _dfs(i);
        }
        fill(begin(used), end(used), false), reverse(begin(vs), end(vs));
        int cnt = 0;
        for (auto &e : vs) {
            if (!used[e]) _rdfs(e, cnt++);
        }
        Graph<true> G(cnt);
        for (int i = 0; i < n; i++) {
            for (auto &e : es[i]) {
                int u = comp[i], v = comp[e.to];
                if (u != v) G.add_edge(u, v);
            }
        }
        return G;
    }

    int operator[](int k) const { return comp[k]; }
};

int main() {
    while (true) {
        int N, M;
        cin >> N >> M;

        if (N == 0) break;

        Bipartite_Matching BM(N, N);

        vector<int> u(M), v(M);
        rep(i, M) {
            cin >> u[i] >> v[i];
            u[i]--, v[i]--;
            BM.add_edge(u[i], v[i]);
        }

        BM.bipartite_matching();

        vector<int> p(N);
        rep(i, N) p[BM.match[i]] = i;

        // print(p);

        Strongly_Connected_Components G(N);
        Union_Find_Tree uf(N);

        rep(i, M) {
            if (u[i] == BM.match[v[i]]) continue;
            G.add_edge(u[i], BM.match[v[i]]);
            uf.unite(u[i], BM.match[v[i]]);
        }
        Graph G2 = G.decompose();
        int K = G2.n;
        vector<bool> used(K, false);

        vector<vector<int>> deg1(N), deg2(N); // 先頭、末端
        vector<int> V(K);                     // SCCの代表元
        // print(V);

        rep(i, N) {
            int c = G[i];
            if (used[c]) continue;
            V[c] = i;
            used[c] = true;
            if (G2.deg1[c] > 0 && G2.deg2[c] == 0) deg1[uf[i]].eb(c);
            if (G2.deg1[c] == 0 && G2.deg2[c] > 0) deg2[uf[i]].eb(c);
        }

        vector<pii> ans;

        vector<int> ids;
        int SX = 0, SY = 0;
        vector<int> xs1, ys1;
        vector<int> xs2, ys2;

        rep(i, N) {
            int X = sz(deg1[i]), Y = sz(deg2[i]);
            if (X == 0 && Y == 0) continue;

            rep(j, X - 1) xs1.eb(deg1[i][j]);
            rep(j, Y - 1) ys1.eb(deg2[i][j]);

            xs2.eb(deg1[i].back());
            ys2.eb(deg2[i].back());
        }

        int L = sz(xs2);
        rep(i, L) {
            int x = xs2[i], y = ys2[(i + 1) % L];
            ans.eb(V[y], p[V[x]]);
        }

        int X = sz(xs1), Y = sz(ys1);
        // if (X == 0 && Y == 0) continue;

        rep(j, min(X, Y)) {
            int x = xs1[j], y = ys1[j];
            ans.eb(V[y], p[V[x]]);
        }

        if (X > Y) {
            rep2(j, Y, X - 1) {
                int x = xs1[j], y = ys2[0];
                ans.eb(V[y], p[V[x]]);
            }
        }
        if (Y > X) {
            rep2(j, X, Y - 1) {
                int x = xs2[0], y = ys1[j];
                ans.eb(V[y], p[V[x]]);
            }
        }

        cout << sz(ans) << '\n';
        each(e, ans) cout << e.first + 1 << ' ' << e.second + 1 << '\n';
    }
}
accuracy: 0.5
time_ms: 140
memory_kb: 8952
final_rank: 11

code_snippet: #ifndef LOCAL
#pragma GCC optimize ("Ofast")
#pragma GCC optimize ("unroll-loops")
#endif

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

using ll=long long;
#define int ll

#define rng(i,a,b) for(int i=int(a);i<int(b);i++)
#define rep(i,b) rng(i,0,b)
#define gnr(i,a,b) for(int i=int(b)-1;i>=int(a);i--)
#define per(i,b) gnr(i,0,b)
#define pb push_back
#define eb emplace_back
#define a first
#define b second
#define bg begin()
#define ed end()
#define all(x) x.bg,x.ed
#define si(x) int(x.size())
#ifdef LOCAL
#define dmp(x) cerr<<__LINE__<<" "<<#x<<" "<<x<<endl
#else
#define dmp(x) void(0)
#endif

template<class t,class u> bool chmax(t&a,u b){if(a<b){a=b;return true;}else return false;}
template<class t,class u> bool chmin(t&a,u b){if(b<a){a=b;return true;}else return false;}

template<class t> using vc=vector<t>;
template<class t> using vvc=vc<vc<t>>;

using pi=pair<int,int>;
using vi=vc<int>;

template<class t,class u>
ostream& operator<<(ostream& os,const pair<t,u>& p){
	return os<<"{"<<p.a<<","<<p.b<<"}";
}

template<class t> ostream& operator<<(ostream& os,const vc<t>& v){
	os<<"{";
	for(auto e:v)os<<e<<",";
	return os<<"}";
}

#define mp make_pair
#define mt make_tuple
#define one(x) memset(x,-1,sizeof(x))
#define zero(x) memset(x,0,sizeof(x))
#ifdef LOCAL
void dmpr(ostream&os){os<<endl;}
template<class T,class... Args>
void dmpr(ostream&os,const T&t,const Args&... args){
	os<<t<<" ";
	dmpr(os,args...);
}
#define dmp2(...) dmpr(cerr,__LINE__,##__VA_ARGS__)
#else
#define dmp2(...) void(0)
#endif

using uint=unsigned;
using ull=unsigned long long;

template<class t,size_t n>
ostream& operator<<(ostream&os,const array<t,n>&a){
	return os<<vc<t>(all(a));
}

template<int i,class T>
void print_tuple(ostream&,const T&){
}

template<int i,class T,class H,class ...Args>
void print_tuple(ostream&os,const T&t){
	if(i)os<<",";
	os<<get<i>(t);
	print_tuple<i+1,T,Args...>(os,t);
}

template<class ...Args>
ostream& operator<<(ostream&os,const tuple<Args...>&t){
	os<<"{";
	print_tuple<0,tuple<Args...>,Args...>(os,t);
	return os<<"}";
}

template<class t>
void print(t x,int suc=1){
	cout<<x;
	if(suc==1)
		cout<<"\n";
	if(suc==2)
		cout<<" ";
}

ll read(){
	ll i;
	cin>>i;
	return i;
}

vi readvi(int n,int off=0){
	vi v(n);
	rep(i,n)v[i]=read()+off;
	return v;
}

pi readpi(int off=0){
	int a,b;cin>>a>>b;
	return pi(a+off,b+off);
}

template<class t,class u>
void print(const pair<t,u>&p,int suc=1){
	print(p.a,2);
	print(p.b,suc);
}

template<class T>
void print(const vector<T>&v,int suc=1){
	rep(i,v.size())
		print(v[i],i==int(v.size())-1?suc:2);
}

template<class T>
void print_offset(const vector<T>&v,ll off,int suc=1){
	rep(i,v.size())
		print(v[i]+off,i==int(v.size())-1?suc:2);
}

template<class T,size_t N>
void print(const array<T,N>&v,int suc=1){
	rep(i,N)
		print(v[i],i==int(N)-1?suc:2);
}

string readString(){
	string s;
	cin>>s;
	return s;
}

template<class T>
T sq(const T& t){
	return t*t;
}

//#define CAPITAL
void yes(bool ex=true){
	#ifdef CAPITAL
	cout<<"YES"<<"\n";
	#else
	cout<<"Yes"<<"\n";
	#endif
	if(ex)exit(0);
	#ifdef LOCAL
	cout.flush();
	#endif
}
void no(bool ex=true){
	#ifdef CAPITAL
	cout<<"NO"<<"\n";
	#else
	cout<<"No"<<"\n";
	#endif
	if(ex)exit(0);
	#ifdef LOCAL
	cout.flush();
	#endif
}
void possible(bool ex=true){
	#ifdef CAPITAL
	cout<<"POSSIBLE"<<"\n";
	#else
	cout<<"Possible"<<"\n";
	#endif
	if(ex)exit(0);
	#ifdef LOCAL
	cout.flush();
	#endif
}
void impossible(bool ex=true){
	#ifdef CAPITAL
	cout<<"IMPOSSIBLE"<<"\n";
	#else
	cout<<"Impossible"<<"\n";
	#endif
	if(ex)exit(0);
	#ifdef LOCAL
	cout.flush();
	#endif
}

constexpr ll ten(int n){
	return n==0?1:ten(n-1)*10;
}

const ll infLL=LLONG_MAX/3;

#ifdef int
const int inf=infLL;
#else
const int inf=INT_MAX/2-100;
#endif

int topbit(signed t){
	return t==0?-1:31-__builtin_clz(t);
}
int topbit(ll t){
	return t==0?-1:63-__builtin_clzll(t);
}
int botbit(signed a){
	return a==0?32:__builtin_ctz(a);
}
int botbit(ll a){
	return a==0?64:__builtin_ctzll(a);
}
int popcount(signed t){
	return __builtin_popcount(t);
}
int popcount(ll t){
	return __builtin_popcountll(t);
}
bool ispow2(int i){
	return i&&(i&-i)==i;
}
ll mask(int i){
	return (ll(1)<<i)-1;
}

bool inc(int a,int b,int c){
	return a<=b&&b<=c;
}

template<class t> void mkuni(vc<t>&v){
	sort(all(v));
	v.erase(unique(all(v)),v.ed);
}

ll rand_int(ll l, ll r) { //[l, r]
	#ifdef LOCAL
	static mt19937_64 gen;
	#else
	static mt19937_64 gen(chrono::steady_clock::now().time_since_epoch().count());
	#endif
	return uniform_int_distribution<ll>(l, r)(gen);
}

template<class t>
void myshuffle(vc<t>&a){
	rep(i,si(a))swap(a[i],a[rand_int(0,i)]);
}

template<class t>
int lwb(const vc<t>&v,const t&a){
	return lower_bound(all(v),a)-v.bg;
}

vvc<int> readGraph(int n,int m){
	vvc<int> g(n);
	rep(i,m){
		int a,b;
		cin>>a>>b;
		//sc.read(a,b);
		a--;b--;
		g[a].pb(b);
		g[b].pb(a);
	}
	return g;
}

vvc<int> readTree(int n){
	return readGraph(n,n-1);
}

//sz: the size of the max matching
//VERIFY: yosupo
template<class E>
struct bipartitematching{
	int n,m;
	vvc<E> g;
	vi dist,vis;
	vi to,ot;
	int sz;
	bipartitematching(int nn,int mm):n(nn),m(mm),g(n),
	dist(n),vis(n),to(n,-1),ot(m,-1),sz(0){}
	void ae(int a,E e){
		g[a].pb(e);
	}
	void bfs(){
		fill(all(dist),-1);
		vi q;
		int h=0;
		auto rc=[&](int v,int d){
			if(v==-1||dist[v]!=-1)return;
			dist[v]=d;
			q.pb(v);
		};
		rep(i,n)if(to[i]==-1)rc(i,0);
		while(h<(int)q.size()){
			int v=q[h++];
			for(auto e:g[v])
				rc(ot[e],dist[v]+1);
		}
	}
	bool dfs(int v){
		if(vis[v])return 0;
		vis[v]=1;
		for(auto e:g[v]){
			if(ot[e]==-1||(dist[v]+1==dist[ot[e]]&&dfs(ot[e]))){
				to[v]=e;
				ot[e]=v;
				return 1;
			}
		}
		return 0;
	}
	void calc(){
		while(1){
			bfs();
			fill(all(vis),0);
			int f=0;
			rep(i,n)if(to[i]==-1)
				if(dfs(i))f++;
			if(f==0)break;
			sz+=f;
		}
	}
};

//s: the number of SCCs.
//bl[i]: SCC to which the vertex i belongs
//idx[i]: list of the vertices in the i-th SCC
//da: DAG of SCCs
//SCCs are numbered in topological order
//VERIFY: yosupo
//AOJGRL3C(except gr)
template<class t>
struct scc{
	const int n;
	const vvc<t>&g;
	vi ord,low,bl,st;
	int s,head;
	vvc<int> idx,da;
	void dfs(int v){
		ord[v]=low[v]=head++;
		st.pb(v);
		for(auto to:g[v]){
			if(ord[to]==-1){
				dfs(to);
				chmin(low[v],low[to]);
			}else if(bl[to]==-1)
				chmin(low[v],ord[to]);
		}
		if(ord[v]==low[v]){
			int c=idx.size();
			idx.eb();
			while(1){
				int a=st.back();
				st.pop_back();
				bl[a]=c;
				idx.back().pb(a);
				if(v==a)break;
			}
		}
	}
	scc(const vvc<t>&gg,bool cgr=true):n(gg.size()),g(gg),
		ord(n,-1),low(n,-1),bl(n,-1){
		head=0;
		rep(i,n)if(ord[i]==-1)
			dfs(i);
		s=idx.size();
		rep(i,n)
			bl[i]=s-1-bl[i];
		reverse(all(idx));
		//construct the graph
		if(cgr){
			vc<bool> u(s);
			da.resize(s);
			rep(i,s){
				for(auto v:idx[i]){
					for(auto to:g[v])if(bl[v]<bl[to]){
						if(!u[bl[to]])
							da[bl[v]].pb(bl[to]);
						u[bl[to]]=1;
					}
				}
				for(auto v:idx[i])
					for(auto to:g[v])
						u[bl[to]]=0;
			}
		}
	}
};

//ICPC 2021 Domestic F (AOJ1653)
//各連結成分が強連結ならばよい
//複数個の成分に分かれていてもかまわない
//if(i==j)continue を消すことで解決可能かもしれない
vc<pi> make_scc_dag(const vvc<int>&g){
	int n=si(g);
	vc<bool> vis(n);
	vc<bool> st(n,true);
	rep(i,n)for(auto j:g[i])st[j]=false;
	auto dfs=[&](auto self,int v)->int{
		if(vis[v])return -1;
		vis[v]=true;
		if(g[v].empty())return v;
		for(auto to:g[v]){
			int z=self(self,to);
			if(z!=-1){
				return z;
			}
		}
		return -1;
	};
	vc<pi> ls;
	rep(i,n)if(st[i]){
		int j=dfs(dfs,i);
		if(j!=-1){
			ls.eb(i,j);
		}
	}
	vc<bool> used(n);
	for(auto [i,j]:ls){
		used[i]=true;
		used[j]=true;
	}
	{
		vc<pi> tmp;
		for(auto [i,j]:ls)if(i!=j)
			tmp.eb(i,j);
		ls.swap(tmp);
	}
	vc<pi> ans;
	rep(i,si(ls)){
		ans.eb(ls[i].b,ls[(i+1)%si(ls)].a);
	}
	vi x,y;
	rep(i,n)if(!used[i]){
		if(st[i])x.pb(i);
		if(g[i].empty())y.pb(i);
	}
	if(si(x)<si(y))x.resize(si(y),ls[0].a);
	if(si(y)<si(x))y.resize(si(x),ls[0].b);
	rep(i,si(x))ans.eb(y[i],x[i]);
	return ans;
}

//ICPC 2021 Domestic F (AOJ1653)
//各連結成分が強連結ならばよい
//複数個の成分に分かれていてもかまわない
vc<pi> make_scc(const vvc<int>&g){
	scc<int> s(g);
	auto z=make_scc_dag(s.da);
	vc<pi> res;
	for(auto [a,b]:z)
		res.eb(s.idx[a][0],s.idx[b][0]);
	return res;
}

void slv(){
	int n,m;cin>>n>>m;
	if(n==0)exit(0);
	vc<pi> es(m);
	bipartitematching<int> bm(n,n);
	rep(i,m){
		int a,b;cin>>a>>b;
		a--;b--;
		es[i]=pi(a,b);
		bm.ae(a,b);
	}
	bm.calc();
	vvc<int> g(n);
	for(auto [a,b]:es){
		b=bm.ot[b];
		if(a!=b)g[a].pb(b);
	}
	auto ans=make_scc(g);
	print(si(ans));
	for(auto [a,b]:ans){
		b=bm.to[b];
		cout<<a+1<<" "<<b+1<<endl;
	}
}

signed main(){
	cin.tie(0);
	ios::sync_with_stdio(0);
	cout<<fixed<<setprecision(20);
	
	//int t;cin>>t;rep(_,t)
	while(1)slv();
}
accuracy: 1
time_ms: 120
memory_kb: 8984
final_rank: 3