Problem:
Gift Exchange Party
A gift exchange party will be held at a school in TKB City.
For every pair of students who are close friends, one gift must be given from one to the other at this party, but not the other way around.
It is decided in advance the gift directions, that is, which student of each pair receives a gift.
No other gift exchanges are made.
If each pair randomly decided the gift direction,
some might receive countless gifts, while some might receive only few or even none.
You'd like to decide the gift directions for all the friend pairs
that minimize the difference between the smallest and the largest numbers of gifts received by a student.
Find the smallest and the largest numbers of gifts received
when the difference between them is minimized.
When there is more than one way to realize that, 
find the way that maximizes the smallest number of received gifts.
Input
The input consists of at most 10 datasets, each in the following format.
n
m
u
1
v
1
...
u
m
v
m
n
is the number of students, and
m
is the number of friendship relations (2 ≤
n
≤ 100, 1 ≤
m
≤
n
(
n
-1)/2). 
Students are denoted by integers between 1 and
n
, inclusive.
The following
m
lines describe the friendship relations: for each
i
, student
u
i
and
v
i
are close friends (
u
i
<
v
i
).
The same friendship relations do not appear more than once.
The end of the input is indicated by a line containing two zeros.
Output
For each dataset, output a single line containing two integers
l
and
h
separated by a single space.
Here,
l
and
h
are the smallest and the largest numbers, respectively, of gifts received by a student.
Sample Input
3 3
1 2
2 3
1 3
4 3
1 2
1 3
1 4
4 6
1 2
1 3
1 4
2 3
3 4
2 4
0 0
Output for the Sample Input
1 1
0 1
1 2

Submitted solutions:

code_snippet: #include <bits/stdc++.h>
using namespace std;
#define FOR(i,k,n) for(int i = (int)(k); i < (int)(n); i++)
#define REP(i,n) FOR(i,0,n)
#define ALL(a) a.begin(), a.end()
#define MS(m,v) memset(m,v,sizeof(m))
typedef long long ll;
typedef long double ld;
typedef vector<int> vi;
typedef vector<string> vs;
typedef pair<int, int> pii;
const int MOD = 1e9 + 7;
template<class T> T &chmin(T &a, const T &b) { return a = min(a, b); }
template<class T> T &chmax(T &a, const T &b) { return a = max(a, b); }
template<class T>
istream& operator >> (istream& is, vector<T>& v)
{
	for (auto &i : v) is >> i;
	return is;
}
template<class T>
ostream& operator<<(ostream& os, vector<T>& v)
{
	const string delimiter = "\n";
	REP(i, v.size())
	{
		os << v[i];
		if (i != v.size() - 1) os << delimiter;
	}
	return os;
}
/*--------------------template--------------------*/

const int INF = 1e6;

typedef int Weight;
typedef int Flow;
struct Edge {
	int src, dest, rev;
	Flow cap;
	Weight cost;
	bool operator < (const Edge &rhs) const
	{
		return cost > rhs.cost;
	}
	Edge(int s, int d) : src(s), dest(d) { ; }
	Edge(int s, int d, int c) : src(s), dest(d), cost(c) { ; }
	Edge(int s, int d, int r, Flow cp, Weight cst) : src(s), dest(d), rev(r), cap(cp), cost(cst) { ; }
};
typedef vector<Edge> Edges;
typedef vector<Edges> Graph;
typedef vector<Weight> Array;
typedef vector<Array> Matrix;
void add_edge(Graph &g, int src, int dest, Flow cap)
{
	g[src].push_back(Edge{ src, dest, (int)g[dest].size(), cap, 0 });
	g[dest].push_back(Edge{ dest, src, (int)g[src].size() - 1, 0, 0 });
}

void add_edge(Graph &g, int from, int to, Flow cap, Weight cost)
{
	g[from].push_back(Edge(from, to, (int)g[to].size(), cap, cost));
	g[to].push_back(Edge(to, from, (int)g[from].size() - 1, 0, -cost));
}

Flow dfs(Graph &g, vector<bool> &used, int v, int t, Flow f)
{
	if (v == t) return f;
	used[v] = true;
	for (Edge& e : g[v])
	{
		if (!used[e.dest] && e.cap > 0)
		{
			Flow d = dfs(g, used, e.dest, t, min(f, e.cap));
			if (d > 0)
			{
				e.cap -= d;
				g[e.dest][e.rev].cap += d;
				return d;
			}
		}
	}
	return 0;
}

Flow ford_fulkerson(Graph &g, int s, int t)
{
	Flow flow = 0;
	for (;;)
	{
		vector<bool> used(g.size(), false);
		Flow f = dfs(g, used, s, t, INF);
		if (f == 0) return flow;
		flow += f;
	}
}


int main()
{
	cin.sync_with_stdio(false); cout << fixed << setprecision(10);
	int n, m;
	while (cin >> n >> m, n)
	{
		Graph g(n + m + 2);
		int src = n + m, sink = n + m + 1;
		REP(i, m) add_edge(g, src, i, 1);
		REP(i, m)
		{
			int a, b; cin >> a >> b;
			a--; b--;
			add_edge(g, i, m + a, 1);
			add_edge(g, i, m + b, 1);
		}
		int tmpcap = -1;
		int mn = -1, mx = -1;
		while(1)
		{
			tmpcap++;
			Graph h = g;
			REP(i, n) add_edge(h, m + i, sink, tmpcap);
			int f = ford_fulkerson(h, src, sink);
			if (tmpcap * n == f)
			{
				mn = tmpcap;
			}
			if (m == f)
			{
				mx = tmpcap;
				break;
			}
		}
		cout << mn << " " << mx << endl;
	}
	return 0;
}
accuracy: 1
time_ms: 650
memory_kb: 4832
final_rank: 16

code_snippet: #pragma GCC optimize("O3")
#pragma GCC optimize("unroll-loops")
#include <bits/stdc++.h>
using namespace std;

// 数値型
using ll = long long;
using ull = unsigned long long;
using ld = long double;
using P = pair<int,int>;
using Pll = pair<ll, ll>;
using Pli = pair<ll, int>;
using Pil = pair<int, ll>;

// vector関連
using vi = vector<int>;
using vvi = vector<vi>;
using vvvi = vector<vvi>;
using vll = vector<ll>;
using vvll = vector<vll>;
using vvvll = vector<vvll>;
template<typename T>
using vc = vector<T>;
template<typename T>
using vvc = vector<vc<T>>;
template<typename T>
using vvvc = vector<vvc<T>>;
template<typename T>
using vvvvc = vector<vvvc<T>>;

// priority_queue
template<typename T>
using pq = priority_queue<T>;
template<typename T>
using pqg = priority_queue<T, vc<T>, greater<T>>;

#define rep(i, n) for(int i = 0; i < (int)(n); i++)
#define FOR(i, a, b) for(int i = a; i < (int)(b); i++)
#define all(a) (a).begin(),(a).end()
#define rall(a) (a).rbegin(),(a).rend()
#define MIN(vec) *min_element(vec)
#define MAX(vec) *max_element(vec)
#define next_perm(vec) (vec).begin(), (vec).end()
#define UNIQUE(vec) vec.erase(unique(vec.begin(), vec.end()), vec.end())
#define el "\n"
#define Yes cout << "Yes" << el
#define No cout << "No" << el
#define YES cout << "YES" << el
#define NO cout << "NO" << el
#define EPS 1e-8
#define Equal(a, b) (fabs((a)-(b)) < EPS) 
#define dbg(x) cerr << #x << "=" << x << el 

// 定数
const string abc = "abcdefghijklmnopqrstuvwxyz";
const string ABC = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
constexpr int INF = 1001001001;
constexpr ll LINF = 1001001001001001001ll;
constexpr int DX[] = {1, 0, -1, 0};
constexpr int DY[] = {0, 1, 0, -1};
constexpr int DX8[] = {1, 0, -1, 0, 1, 1, -1, -1};
constexpr int DY8[] = {0, 1, 0, -1, 1, -1, 1, -1};

template<typename T1, typename T2>
ostream &operator<< (ostream &os, pair<T1, T2> p) {
    os << "{" << p.first << "," << p.second << "}";
    return os;
}
template<typename T>
ostream &operator<< (ostream &os, vc<T> &vec) {
    int sz = vec.size();
    rep(i, sz){
        os << vec[i] << (i==sz-1?"":" ");
    }
    return os;
}

template<typename T1, typename T2>
istream &operator>> (istream &is, pair<T1, T2> &p) {
    is >> p.first >> p.second;
    return is;
}
template<typename T>
istream &operator>> (istream &is, vc<T> &vec) {
    int sz = vec.size();
    rep(i, sz) { is >> vec[i]; }
    return is;
}
/// @brief aとｂの最大値をaに格納。更新があったかbool値を返す
/// @tparam T1 
/// @tparam T2 
/// @param a 
/// @param b 
/// @return bool
template<typename T1, typename T2>
inline bool chmax(T1 &a, T2 b){
    bool ret = a<b;
    if(ret) a = b;
    return ret;
}

/// @brief aとｂの最小値をaに格納。更新があったかbool値を返す
/// @tparam T1 
/// @tparam T2 
/// @param a 
/// @param b 
/// @return bool
template<typename T1, typename T2>
inline bool chmin(T1 &a, T2 b){
    bool ret = a>b;
    if(ret) {a = b;}
    return ret;
}

inline void YesNo(bool flag){
    if(flag) {Yes;}
    else {No;}
    return;
}

inline void YESNO(bool flag){
    if(flag) {YES;}
    else {NO;}
    return;
}

inline bool outof(ll x, ll xlim){
    return (x<0 || x>=xlim);
}

template<typename T>
inline T sqnorm(T x, T y){
    return x*x+y*y;
}

/// @brief char->int
/// @param c 
/// @return int
inline int ctoi(char c){
    return c-'0';
}

/// @brief xを素因数分解
/// @param x 
/// @return vector<Pli>, 素因数の昇順に {p, cnt}
vector<Pli> prime_fact(ll x){
    vector<Pli> ret;
    for(ll i=2; i*i<=x; i++){
        if(x%i == 0){
            ret.emplace_back(i, 0);
            while(x%i == 0){
                ret.back().second++;
                x /= i;
            }
        }
    }
    if(x != 1) ret.emplace_back(x, 1);
    return ret;
}

/// @brief xの約数列挙
/// @param x 
/// @return vll, 約数の昇順
vll divisor_enum(ll x){
    vector<ll> ret;
    for(ll i=1; i*i<=x; i++){
        if(x%i == 0){
            ret.push_back(x/i);
            ret.push_back(i);
        }
    }
    sort(all(ret));
    UNIQUE(ret);
    return ret;
}

/// @brief 繰り返し二乗法。
/// @tparam T 
/// @param x 
/// @param k 
/// @param op 
/// @param e 
/// @return 
template<typename T>
T pow_t(T x, ll k, T (*op)(T, T), T (*e)()){
    T ret = e();
    while(k){
        if(k&1) ret *= x;
        x *= x;
        k >>= 1;
    }
    return ret;
}

ll powll(ll x, ll k){
    return pow_t<ll>(x, k, [](ll a, ll b) -> ll{return a*b;}, []() -> ll{return 1;});
}

inline int pop_cnt(ll x) { return __builtin_popcountll(x); }
inline int top_bit(ll x) { return (x==0?-1:63-__builtin_clzll(x));}

template< typename flow_t >
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;

  Dinic(int V) : INF(numeric_limits< flow_t >::max()), 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 bfs(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 dfs(int idx, const int t, flow_t flow) {
    if(idx == t) return flow;
    for(int &i = iter[idx]; i < graph[idx].size(); i++) {
      edge &e = graph[idx][i];
      if(e.cap > 0 && min_cost[idx] < min_cost[e.to]) {
        flow_t d = dfs(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(bfs(s, t)) {
      iter.assign(graph.size(), 0);
      flow_t f = 0;
      while((f = dfs(s, t, INF)) > 0) flow += f;
    }
    return flow;
  }

  void output() {
    for(int i = 0; i < graph.size(); i++) {
      for(auto &e : graph[i]) {
        if(e.isrev) continue;
        auto &rev_e = graph[e.to][e.rev];
        cout << i << "->" << e.to << " (flow: " << rev_e.cap << "/" << e.cap + rev_e.cap << ")" << endl;
      }
    }
  }
};


template< typename flow_t, template< typename > class F >
struct MaxFlowLowerBound {
  F< flow_t > flow;
  vector< flow_t > in, up;
  typename F< flow_t >::edge *latte, *malta;
  int X, Y, V;
  flow_t sum;

  MaxFlowLowerBound(int V) : V(V), flow(V + 2), X(V), Y(V + 1), sum(0), in(V) {}

  void add_edge(int from, int to, flow_t low, flow_t high) {
    assert(from != to);
    flow.add_edge(from, to, high - low, up.size());
    in[from] -= low;
    in[to] += low;
    up.emplace_back(high);
  }

  void build() {
    for(int i = 0; i < V; i++) {
      if(in[i] > 0) {
        flow.add_edge(X, i, in[i]);
        sum += in[i];
      } else if(in[i] < 0) {
        flow.add_edge(i, Y, -in[i]);
      }
    }
  }

  bool can_flow(int s, int t) {
    assert(s != t);
    flow.add_edge(t, s, flow.INF);
    latte = &flow.graph[t].back();
    malta = &flow.graph[s].back();
    return can_flow();
  }

  bool can_flow() {
    build();
    auto ret = flow.max_flow(X, Y);
    return ret >= sum;
  }

  flow_t max_flow(int s, int t) {
    if(can_flow(s, t)) {
      return flow.max_flow(s, t);
    } else {
      return -1;
    }
  }

  flow_t min_flow(int s, int t) {
    if(can_flow(s, t)) {
      auto ret = flow.INF - latte->cap;
      latte->cap = malta->cap = 0;
      return ret - flow.max_flow(t, s);
    } else {
      return -1;
    }
  }

  void output(int M) {
    vector< flow_t > ans(M);
    for(int i = 0; i < flow.graph.size(); i++) {
      for(auto &e : flow.graph[i]) {
        if(!e.isrev && ~e.idx) ans[e.idx] = up[e.idx] - e.cap;
      }
    }
    for(auto &p : ans) cout << p << endl;
  }
};




void main2();

int main(){
    ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    main2();
}

bool solve(){
    int n, m;
    cin >> n >> m;
    if(n == 0) return false;
    vc<P> es(m);
    rep(i, m){
        int u, v;
        cin >> u >> v;
        u--;v--;
        es[i] = {u, v};
    }
    int ma = 0;
    P ans = {INF, 2*INF};
    rep(mi, n+1){
        bool ng = false;
        chmax(ma, mi);
        while(true){
            vi vvv(1, 2+n+m);
            MaxFlowLowerBound<int, Dinic> mf(n+m+2);
            rep(i, m){
                auto [u, v] = es[i];
                mf.add_edge(n+m, i, 1, 1);
                mf.add_edge(i, m+u, 0, 1);
                mf.add_edge(i, m+v, 0, 1);
            }
            rep(i, n){
                mf.add_edge(m+i, n+m+1, mi, ma);
            }
            auto flow = mf.max_flow(n+m, n+m+1);
            if(flow >= m) break;
            if(ma == n){
                ng = true;
                break;
            }
            ma++;
        }
        if(ng) break;
        if(ans.second - ans.first > ma - mi){
            ans.first = mi;
            ans.second = ma;
        }
    }
    cout << ans.first << " " << ans.second << el;
    
    
    return true;
}

void main2(){
    while(solve()){
        ;
    }
}
accuracy: 1
time_ms: 220
memory_kb: 4608
final_rank: 8

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;
struct HLD {
    vector<vector<int>> g;
    vector<int> sz, in, out, par, head, dep, ord;
    HLD(vector<vector<int>> &g_, int root = 0)
        : g(g_), sz((int)g_.size()), in((int)g_.size()), out((int)g_.size()), par((int)g_.size()), head((int)g_.size(), root), dep((int)g_.size()) {
        dfs_sz(root, -1);
        dfs_hld(root, -1);
    }
    void dfs_sz(int v, int p) {
        par[v] = p;
        sz[v] = 1;
        if (g[v].size() && g[v][0] == p) swap(g[v][0], g[v].back());
        for (auto &i : g[v]) {
            if (i != p) {
                dep[i] = dep[v] + 1;
                dfs_sz(i, v);
                sz[v] += sz[i];
                if (sz[g[v][0]] < sz[i]) swap(g[v][0], i);
            }
        }
    }
    void dfs_hld(int v, int p) {
        in[v] = ord.size();
        ord.push_back(v);
        for (auto i : g[v]) {
            if (i != p) {
                if (i == g[v][0]) {
                    // Heavy
                    head[i] = head[v];
                } else {
                    // Light
                    head[i] = i;
                }
                dfs_hld(i, v);
            }
        }
        out[v] = ord.size();
    }
    int lca(int u, int v) {
        while (1) {
            if (in[u] > in[v]) swap(u, v);
            if (head[u] == head[v]) return u;
            v = par[head[v]];
        }
    }
    int dist(int u, int v) { return dep[u] + dep[v] - 2 * dep[lca(u, v)]; }
    int la(int v, int d) {
        while (v != -1) {
            int u = head[v];
            if (in[v] - d >= in[u]) return ord[in[v] - d];
            d -= in[v] - in[u] + 1, v = par[u];
        }
        return -1;
    }
    int jump(int from, int to, int d) {
        int l = lca(from, to);
        if (d <= dep[from] - dep[l]) return la(from, d);
        d -= dep[from] - dep[l];
        if (d <= dep[to] - dep[l]) return la(to, dep[to] - dep[l] - d);
        return -1;
    }
};
template <typename T, typename U>
inline istream &operator>>(istream &is, pair<T, U> &rhs) {
    return is >> rhs.first >> rhs.second;
}
template <typename T>
inline istream &operator>>(istream &is, vector<T> &v) {
    for (auto &e : v) is >> e;
    return is;
}
template <typename T, typename U>
inline ostream &operator<<(ostream &os, const pair<T, U> &rhs) {
    return os << rhs.first << " " << rhs.second;
}
template <typename T>
inline ostream &operator<<(ostream &os, const vector<T> &v) {
    for (auto itr = v.begin(), end_itr = v.end(); itr != end_itr;) {
        os << *itr;
        if (++itr != end_itr) os << " ";
    }
    return os;
}

struct FunctionMo {
    int n;
    vector<pair<int, int>> xy;

    explicit FunctionMo(int n) : n(n) {}

    void add(int x, int y) { xy.emplace_back(x, y); }

    template <typename AX, typename SX, typename AY, typename SY, typename O>
    void build(const AX &add_x, const SX &sub_x, const AY &add_y, const SY &sub_y, const O &out) {
        int q = (int)xy.size();
        int bs = max<int>(1, sqrt(n));
        vector<int> ord(q);
        iota(begin(ord), end(ord), 0);
        sort(begin(ord), end(ord), [&](int a, int b) {
            int ablock = xy[a].first / bs, bblock = xy[b].first / bs;
            if (ablock != bblock) return ablock < bblock;
            return (ablock & 1) ? xy[a].second > xy[b].second : xy[a].second < xy[b].second;
        });
        int x = 0, y = 0;
        for (auto idx : ord) {
            while (x > xy[idx].first) add_x(x++, y);
            while (y < xy[idx].second) add_y(x, y++);
            while (x < xy[idx].first) sub_x(x--, y);
            while (y > xy[idx].second) sub_y(x, y--);
            out(idx);
        }
    }

    template <typename A, typename E, typename O>
    void build(const A &add, const E &erase, const O &out) {
        build(add, add, erase, erase, out);
    }
};
struct UnionFind {
    vector<int> par, siz;
    UnionFind(int x) {
        par.resize(x);
        siz.resize(x);
        for (int i = 0; i < x; i++) {
            par[i] = i;
            siz[i] = 1;
        }
    }
    int find(int x) {
        if (par[x] == x) return x;
        return par[x] = find(par[x]);
    }
    bool unite(int x, int y) {
        x = find(x), y = find(y);
        if (x == y) return false;
        if (siz[x] < siz[y]) swap(x, y);
        par[y] = x;
        siz[x] += siz[y];

        return true;
    }
    bool same(int x, int y) { return find(x) == find(y); }
    int size(int x) { return siz[find(x)]; }
};
template <class T>
struct BIT {
    // 1-indexed
    int n, beki = 1;
    vector<T> bit;
    BIT(int x) {
        bit.resize(x + 1, 0);
        n = x;
        while (beki * 2 <= n) beki *= 2;
    }
    T sum(int i) {
        T res = 0;
        while (i > 0) {
            res += bit[i];
            i -= i & -i;
        }
        return res;
    }
    T sum(int l, int r) {
        //[l,r]
        return sum(r) - (l == 0 ? 0 : sum(l - 1));
    }
    void add(int i, T x) {
        while (i <= n) {
            bit[i] += x;
            i += i & -i;
        }
    }
    int lowerbound(T w) {
        if (w <= 0) return 0;
        int x = 0;
        for (int k = beki; k > 0; k >>= 1) {
            if (x + k <= n && bit[x + k] < w) {
                w -= bit[x + k];
                x += k;
            }
        }
        return x + 1;
    }
};
template <typename T, typename Compare = less<T>>
vector<pair<int, T>> monotone_minima(int H, int W, const function<T(int, int)> &f, const Compare &comp = Compare()) {
    vector<pair<int, T>> dp(H);
    function<void(int, int, int, int)> dfs = [&](int top, int bottom, int left, int right) {
        if (top > bottom) return;
        int line = (top + bottom) / 2;
        T ma;
        int mi = -1;
        for (int i = left; i <= right; i++) {
            T cst = f(line, i);
            if (mi == -1 || comp(cst, ma)) {
                ma = cst;
                mi = i;
            }
        }
        dp[line] = make_pair(mi, ma);
        dfs(top, line - 1, left, mi);
        dfs(line + 1, bottom, mi, right);
    };
    dfs(0, H - 1, 0, W - 1);
    return dp;
}
#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

void solve() {
    int n, m;
    cin >> n >> m;
    if (n == 0) exit(0);
    vector<int> u(m), v(m);
    rep(i, m) {
        cin >> u[i] >> v[i];
        u[i]--, v[i]--;
    }
    atcoder::mf_graph<int> g_tmp(n + m + 4);
    int S = n + m, T = S + 1, e1 = T + 1, e2 = e1 + 1;
    rep(i, m) g_tmp.add_edge(S, n + i, 1);
    rep(i, m) {
        g_tmp.add_edge(n + i, u[i], 1);
        g_tmp.add_edge(n + i, v[i], 1);
    }
   // g_tmp.add_edge(e1, S, INF);
   // g_tmp.add_edge(T, e2, INF);
    int d = INF, ansl = -1, ansu = -1;
    for (int lb = m / n; lb >= 0; lb--) {
        int ok = n+1 , ng = lb - 1;
        while (ok - ng > 1) {
            int ub = (ok + ng) / 2;
            auto g = g_tmp;
            int cp=m;
            rep(i, n) {
                // i->T,[lb,ub]
                g.add_edge(i, T, lb);
                g.add_edge(i, e2, ub-lb);
                cp-=lb;
            }
            g.add_edge(e2,T,cp);
            if (g.flow(S, T) == m)
                ok = ub;
            else
                ng = ub;
        }
        if (ok == n + 1) continue;
        // cout << lb << " " << ok << endl;
        if (chmin(d, ok - lb)) {
            ansl = lb;
            ansu = ok;
        }
    }
    cout << ansl << " " << ansu << endl;
}
int main() {
    cin.tie(0);
    ios::sync_with_stdio(false);
    int t = 1;
    // cin >> t;
    while (t) solve();
}
accuracy: 1
time_ms: 130
memory_kb: 4760
final_rank: 14

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

#define OVERRIDE(a,b,c,d,...) d
#define REP2(i,n) for (i32 i = 0; i < (i32)(n); ++i)
#define REP3(i,m,n) for (i32 i = (i32)(m); i < (i32)(n); ++i)
#define REP(...) OVERRIDE(__VA_ARGS__, REP3, REP2)(__VA_ARGS__)
#define PER2(i,n) for (i32 i = (i32)(n)-1; i >= 0; --i)
#define PER3(i,m,n) for (i32 i = (i32)(n)-1; i >= (i32)(m); --i)
#define PER(...) OVERRIDE(__VA_ARGS__, PER3, PER2)(__VA_ARGS__)
#define ALL(x) begin(x), end(x)
#define LEN(x) (i32)(x.size())
using namespace std;
using u32 = unsigned int;
using u64 = unsigned long long;
using i32 = signed int;
using i64 = signed long long;
using f64 = double;
using f80 = long double;
using pi = pair<i32, i32>;
using pl = pair<i64, i64>;
template <typename T>
using V = vector<T>;
template <typename T>
using VV = V<V<T>>;
template <typename T>
using VVV = V<V<V<T>>>;
template <typename T>
using VVVV = V<V<V<V<T>>>>;
template <typename T>
using PQR = priority_queue<T, V<T>, greater<T>>;
template <typename T>
bool chmin(T &x, const T &y) {
    if (x > y) {
        x = y;
        return true;
    }
    return false;
}
template <typename T>
bool chmax(T &x, const T &y) {
    if (x < y) {
        x = y;
        return true;
    }
    return false;
}
template <typename T>
i32 lob(const V<T> &arr, const T &v) {
    return (i32)(lower_bound(ALL(arr), v) - arr.begin());
}
template <typename T>
i32 upb(const V<T> &arr, const T &v) {
    return (i32)(upper_bound(ALL(arr), v) - arr.begin());
}
template <typename T>
V<i32> argsort(const V<T> &arr) {
    V<i32> ret(arr.size());
    iota(ALL(ret), 0);
    sort(ALL(ret), [&](i32 i, i32 j) -> bool {
        if (arr[i] == arr[j]) {
            return i < j;
        } else {
            return arr[i] < arr[j];
        }
    });
    return ret;
}
[[maybe_unused]] constexpr i32 INF = 1000000100;
[[maybe_unused]] constexpr i64 INF64 = 3000000000000000100;
struct SetUpIO {
    SetUpIO() {
        ios::sync_with_stdio(false);
        cin.tie(nullptr);
        cout << fixed << setprecision(15);
    }
} set_up_io;
void scan(char &x) { cin >> x; }
void scan(u32 &x) { cin >> x; }
void scan(u64 &x) { cin >> x; }
void scan(i32 &x) { cin >> x; }
void scan(i64 &x) { cin >> x; }
void scan(string &x) { cin >> x; }
template <typename T>
void scan(V<T> &x) {
    for (T &ele : x) {
        scan(ele);
    }
}
void read() {}
template <typename Head, typename... Tail>
void read(Head &head, Tail &...tail) {
    scan(head);
    read(tail...);
}
#define CHAR(...) char __VA_ARGS__; read(__VA_ARGS__);
#define U32(...) u32 __VA_ARGS__; read(__VA_ARGS__);
#define U64(...) u64 __VA_ARGS__; read(__VA_ARGS__);
#define I32(...) i32 __VA_ARGS__; read(__VA_ARGS__);
#define I64(...) i64 __VA_ARGS__; read(__VA_ARGS__);
#define STR(...) string __VA_ARGS__; read(__VA_ARGS__);
#define VEC(type,name,size) V<type> name(size); read(name);
#define VVEC(type,name,size1,size2) VV<type> name(size1, V<type>(size2)); read(name);
#define DBG(...) (void)0

#define ATCODER_MAXFLOW_HPP 1

#define ATCODER_INTERNAL_QUEUE_HPP 1
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 atcoder {
template <class Cap> struct mf_graph {
  public:
    mf_graph() : _n(0) {}
    explicit 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) return res;
            }
            level[v] = _n;
            return res;
        };
        Cap flow = 0;
        while (flow < flow_limit) {
            bfs();
            if (level[t] == -1) break;
            std::fill(iter.begin(), iter.end(), 0);
            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;
};
}
void solve(i32 n, i32 m) {
    V<i32> u(m), v(m);
    REP(i, m) {
        read(u[i], v[i]);
        --u[i];
        --v[i];
    }
    pi ans(0, n - 1);
    auto judge = [&](i32 l, i32 r) -> bool {
        atcoder::mf_graph<i32> g(n + m + 3);
        i32 s = n + m, t = n + m + 1, p = n + m + 2;
        REP(i, m) {
            g.add_edge(u[i], n + i, 1);
            g.add_edge(v[i], n + i, 1);
        }
        REP(i, n) { g.add_edge(p, i, r - l); }
        g.add_edge(s, p, m - n * l);
        REP(i, n) { g.add_edge(s, i, l); }
        REP(i, m) { g.add_edge(n + i, t, 1); }
        return g.flow(s, t) == m;
    };
    REP(l, n) {
        if (m < n * l) {
            break;
        }
        i32 ok = n, ng = l - 1;
        while (ok - ng > 1) {
            i32 mid = (ok + ng) / 2;
            if (judge(l, mid)) {
                ok = mid;
            } else {
                ng = mid;
            }
        }
        if (ok != n) {
            if (ok - l < ans.second - ans.first) {
                ans.first = l;
                ans.second = ok;
            } else if (ok - l == ans.second - ans.first && l >= ans.first) {
                ans.first = l;
                ans.second = ok;
            }
        }
    }
    cout << ans.first << ' ' << ans.second << endl;
}
int main() {
    while (1) {
        I32(n, m);
        if (n == 0) {
            break;
        }
        solve(n, m);
    }
}
accuracy: 1
time_ms: 250
memory_kb: 4200
final_rank: 3

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

namespace atcoder {

// Implement (union by size) + (path compression)
// Reference:
// Zvi Galil and Giuseppe F. Italiano,
// Data structures and algorithms for disjoint set union problems
struct dsu {
  public:
    dsu() : _n(0) {}
    explicit 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;
    // root node: -1 * component size
    // otherwise: parent
    std::vector<int> parent_or_size;
};

}  // namespace atcoder
/* BOOST MULTIPRECISION
#include<boost/multiprecision/cpp_int.hpp>
using namespace boost::multiprecision;
//*/

typedef long long ll;

#define rep(i, s, n) for (int i = (int)(s); i < (int)(n); i++)
#define rrep(i, s, n) for (int i = (int)(n)-1; i >= (int)(s); i--)
#define all(v) begin(v),end(v)

template <typename T> bool chmin(T &a, const T &b) {
	if (a <= b) return false;
	a = b;
	return true;
}

template <typename T> bool chmax(T &a, const T &b) {
	if (a >= b) return false;
	a = b;
	return true;
}

template <typename T> T max(vector<T> &a){
	assert(!a.empty());
	T ret = a[0];
	for (int i=0; i<(int)a.size(); i++) chmax(ret, a[i]);
	return ret;
}

template <typename T> T min(vector<T> &a){
	assert(!a.empty());
	T ret = a[0];
	for (int i=0; i<(int)a.size(); i++) chmin(ret, a[i]);
	return ret;
}

template <typename T> T sum(vector<T> &a){
	T ret = 0;
	for (int i=0; i<(int)a.size(); i++) ret += a[i];
	return ret;
}

// importbisect
template <typename T>
int bisect_left(vector<T> &X, T v){
	return lower_bound(X.begin(), X.end(), v) - X.begin();
}

template <typename T>
int bisect_right(vector<T> &X, T v){
	return upper_bound(X.begin(), X.end(), v) - X.begin();
}
// ----

// defcomp
template <typename T>
vector<T> compress(vector<T> &X) {
	vector<T> vals = X;
	sort(vals.begin(), vals.end());
	vals.erase(unique(vals.begin(), vals.end()), vals.end());
	return vals;
}
// -----
struct edge {
	int to, cap, rev;
};


void solve(int n, int m) {
	vector<int> u(m), v(m);
	rep(i,0,m) {
		cin >> u[i] >> v[i];
		u[i]--; v[i]--;
	}

	vector<vector<edge>> mf(n+m+2, vector<edge>(0));
	auto add_edge = [&](int i, int j, int cap) -> void {
		mf[i].push_back(
			edge{j, cap, (int)mf[j].size()}
		);
		mf[j].push_back(
			edge{i, 0, (int)mf[i].size()-1}
		);
	};

	vector<bool> seen(n+m+2);
	vector<pair<int,int>> road;
	auto dfs = [&](auto self, int i, int j, int dat) -> int {
		if (i == j) return dat;
		seen[i] = 1;

		rep(x,0,(int)mf[i].size()) {
			auto e = mf[i][x];
			if (e.cap == 0) continue;
			if (seen[e.to]) continue;
			int r = self(self, e.to, j, min(e.cap, dat));
			if (r > 0) {
				road.push_back(pair(i, x));
				return r;
			}
		}

		return 0;
	};

	auto flow = [&](int i, int j) -> int {
		int f = 0;
		while (true) {
			fill(all(seen), false);
			vector<pair<int,int>>().swap(road);
			int g = dfs(dfs, i, j, (int)1e9);
			f += g;
			if (g == 0) break;
			for (auto [r,x]: road) {
				mf[r][x].cap -= g;
				mf[mf[r][x].to][mf[r][x].rev].cap += g;
				//assert(mf[mf[r][x].to][mf[r][x].rev].rev==x);
			}
		};
		return f;
	};

	int ans = (int)1e9;
	int ansmin = -1;
	int ansmax = -1;


	rep(mins,0,n+1) {
		rep(i,0,n+m+2) {
			vector<edge>().swap(mf[i]);
		}

		vector<int> jun(n);
		vector<int> rev(n);

		rep(i,0,m){
			add_edge(i,m+u[i],1);
			add_edge(i,m+v[i],1);
			add_edge(n+m,i,1);
		}

		rep(i,0,n){
			add_edge(i+m,n+m+1,mins);
			jun[i] = (int)mf[i+m].size()-1;
			rev[i] = (int)mf[n+m+1].size()-1;
		}

		int f = flow(n+m,n+m+1);

		bool mode = 1;
		rep(i,0,n) {
			if (mf[i+m][jun[i]].cap != 0) {
				mode = 0;
				break;
			}
			assert(mf[n+m+1][rev[i]].cap == mins);
			mf[n+m+1][rev[i]].cap = 0;
		}
		if (!mode) break;

		int maxs = mins;
		while (f != m) {
			rep(i,0,n){
				mf[i+m][jun[i]].cap += 1;
				//cout << mf[i+m][jun[i]].cap << endl;
			}
			maxs++;
			f += flow(n+m,n+m+1);
			
			//cout << mins << ' ' << maxs << ' ' << f << endl;
		}
		
		if (ans >= maxs-mins){
			ansmin = mins;
			ansmax = maxs;
			ans = maxs-mins;
		}
	}

	cout << ansmin << ' ' << ansmax << endl;
}

int main(){
	ios_base::sync_with_stdio(false);
	cin.tie(NULL);
	while(true) {
		int n, m;
		cin >> n >> m;
		if (n == 0) break;
		solve(n, m);
	}
}
accuracy: 1
time_ms: 1990
memory_kb: 4224
final_rank: 15

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


struct Flow{
	struct edge {long long to, cap, rev; };
	vector<vector<edge>> G;
    vector<int> level;
    vector<int> iter;
	int MAX_V;
 
	Flow(int v){
		G.resize(v);
		iter.resize(v);
        level.resize(v , -1);
		MAX_V = v;
	}
 
	void add_edge(int from, int to, long long cap){
		G[from].push_back(edge{to, cap, (long long)G[to].size()});
		G[to].push_back(edge{from, 0, (long long)G[from].size()-1});
	};
 
    void bfs(int s){
        for(int i = 0; i < MAX_V; i++)level[i] = -1;
        queue<int> que;
        que.push(s);
        level[s] = 0;
        while(!que.empty()){
            int v = que.front(); que.pop();
            for(int i = 0; i < (int)(G[v].size()); i++){
                edge &e = G[v][i];
                if(e.cap > 0 && level[e.to] < 0){
                    level[e.to] = level[v] + 1;
                    que.push(e.to);
                }
            }
        }
    }
 
	long long dfs(int v, int t, long long f){
		if(v == t)return f;
		for(int &i = iter[v]; i < (int)(G[v].size()); i++){
			edge &e = G[v][i];
			if(e.cap > 0 && level[v] < level[e.to]){
				long long d = dfs(e.to, t, min(f, e.cap));
				if(d > 0){
					e.cap -= d;
					G[e.to][e.rev].cap += d;
					return d;
				}
			}
		}
		return 0;
	}
 
	long long max_flow(int s, int t){
		long long flow = 0;
		while(1){
			bfs(s);
			if(level[t] < 0)return flow;
            for(int i = 0; i < MAX_V; i++)iter[i] = 0;
			long long f;
            while((f = dfs(s , t , 1LL<<60)) > 0)flow += f;
		}
		return 0;
	}
};



int main(){
	while(1){
		int N,M; cin >> N >> M;
		if(N == 0)break;
		vector<int> v(M) , u(M);
		for(int i = 0; i < M; i++){
			cin >> v[i] >> u[i];
			--v[i];
			--u[i];
		}
		auto ok = [&](int low , int high)->bool{
			Flow f(N+M+4);
			int S = N+M+3;
			int T = S-1;
			int S2 = T-1;
			int T2 = S2-1;
			for(int i = 0; i < M; i++){
				f.add_edge(S , i , 1);
				f.add_edge(i , M+v[i] , 1);
				f.add_edge(i , M+u[i] , 1);
			}
			for(int i = 0; i < N; i++){
				f.add_edge(M+i , T , high - low);
				f.add_edge(S2 , T , low);
				f.add_edge(M+i , T2 , low);
			}
			ll FLOW = 0;
			FLOW += f.max_flow(S2 , T2);
			FLOW += f.max_flow(S2 , T);
			FLOW += f.max_flow(S , T2);
			FLOW += f.max_flow(S , T);
			if(FLOW < M)return 0;
			for(auto e:f.G[S2]){
				if(e.cap > 0)return 0;
			}
			for(auto e:f.G[T2]){
				if(e.cap < low)return 0;
			}
			return 1;
		};
		int LO = -1 , HI = M;
		int hi = 1;
		for(int lo = 0; lo < N; lo++){
			hi = max(hi , lo);
			while(hi <= N && !ok(lo, hi))hi++;
			if(hi == N+1)break;
			if(HI - LO >= hi - lo){
				HI = hi;
				LO = lo;
			}
		}
		/*
		for(int i = 0; i < N; i++){
			for(int j = i; j < N; j++){
				cout << i << ' ' << j << ' ' << ok(i,j) << endl;
			}
		}
		*/
		cout << LO << ' ' << HI << endl;
	}
}
accuracy: 1
time_ms: 230
memory_kb: 4608
final_rank: 9

code_snippet: #include <bits/stdc++.h>
using namespace std;
#define ll long long
#define pii pair<int, int>
#define pll pair<ll, ll>
#define vi vector<int>
#define vl vector<ll>
#define ov4(a, b, c, d, name, ...) name
#define rep3(i, a, b, c) for(ll i = (a); i < (b); i += (c))
#define rep2(i, a, b) rep3(i, a, b, 1)
#define rep1(i, n) rep2(i, 0, n)
#define rep0(n) rep1(aaaaa, n)
#define rep(...) ov4(__VA_ARGS__, rep3, rep2, rep1, rep0)(__VA_ARGS__)
#define per(i, a, b) for(ll i = (a) - 1; i >= (b); i--)
#define fore(e, v) for(auto &&e : v)
#define all(a) begin(a), end(a)
#define si(a) (int)(size(a))
#define lb(v, x) (lower_bound(all(v), x) - begin(v))
#define eb emplace_back

template <typename T, typename S> bool chmin(T &a, const S &b) { return a > b ? a = b, 1 : 0; }
template <typename T, typename S> bool chmax(T &a, const S &b) { return a < b ? a = b, 1 : 0; }

const int INF = 1e9 + 100;
const ll INFL = 3e18 + 100;

#define i128 __int128_t

struct _ {
    _() { cin.tie(0)->sync_with_stdio(0), cout.tie(0); }
} __;
template <typename T> struct Dinic {
    const T INF;

    struct edge {
        int to;
        T cap;
        int rev;
        bool isrev;
        int idx;
    };

    vector<vector<edge>> g;
    vector<int> c, iter;
    Dinic(int V) : INF(numeric_limits<T>::max()), g(V) {}
    void add_edge(int from, int to, T cap, int idx = -1) {
        g[from].emplace_back((edge){to, cap, si(g[to]), false, idx});
        g[to].emplace_back((edge){from, 0, si(g[from]) - 1, true, idx});
    }

    bool bfs(int s, int t) {
        c.assign(si(g), -1);
        queue<int> q;
        c[s] = 0;
        q.push(s);
        while(!q.empty() && c[t] == -1) {
            int x = q.front();
            q.pop();
            fore(e, g[x]) {
                if(e.cap > 0 && c[e.to] == -1) {
                    c[e.to] = c[x] + 1;
                    q.push(e.to);
                }
            }
        }
        return c[t] != -1;
    }

    T dfs(int x, int t, T flow) {
        if(x == t) return flow;
        for(int &i = iter[x]; i < si(g[x]); i++) {
            edge &e = g[x][i];
            if(e.cap > 0 && c[x] < c[e.to]) {
                T d = dfs(e.to, t, min(flow, e.cap));
                if(d > 0) {
                    e.cap -= d;
                    g[e.to][e.rev].cap += d;
                    return d;
                }
            }
        }
        return 0;
    }

    T max_flow(int s, int t) {
        T flow = 0;
        while(bfs(s, t)) {
            iter.assign(si(g), 0);
            T f = 0;
            while((f = dfs(s, t, INF)) > 0) flow += f;
        }
        return flow;
    }

    // void output() {
    //     for(int i = 0; i < g.size(); i++) {
    //         for(auto &e : g[i]) {
    //             if(e.isrev) continue;
    //             auto &rev_e = g[e.to][e.rev];
    //             cout << i << "->" << e.to << " (flow: " << rev_e.cap << "/" << e.cap + rev_e.cap << ")" << endl;
    //         }
    //     }
    // }
};

template <typename T> struct lrFlow {
    Dinic<T> flow;
    vector<T> in, up;
    int X, Y, n;
    T sum;
    typename Dinic<T>::edge *p, *q;

    lrFlow(int n) : n(n), X(n), Y(n + 1), sum(0), in(n), flow(n + 2) {}

    void add_edge(int from, int to, T low, T high) {
        flow.add_edge(from, to, high - low, si(up));
        in[from] -= low, in[to] += low;
        up.eb(high);
    }

    void build() {
        rep(i, n) {
            if(in[i] > 0) {
                flow.add_edge(X, i, in[i]);
                sum += in[i];
            } else if(in[i] < 0) {
                flow.add_edge(i, Y, -in[i]);
            }
        }
    }

    bool can_flow(int s, int t) {
        flow.add_edge(t, s, flow.INF);
        p = &flow.g[t].back();
        q = &flow.g[s].back();
        return can_flow();
    }

    bool can_flow() {
        build();
        auto ret = flow.max_flow(X, Y);
        return ret >= sum;
    }

    T max_flow(int s, int t) {
        if(can_flow(s, t)) {
            return flow.max_flow(s, t);
        } else {
            return -1;
        }
    }

    T min_flow(int s, int t) {
        if(can_flow(s, t)) {
            auto ret = flow.INF - p->cap;
            p->cap = q->cap = 0;
            return ret - flow.max_flow(t, s);
        } else {
            return -1;
        }
    }

    // void output(int M) {
    //     vector<flow_t> ans(M);
    //     for(int i = 0; i < flow.graph.size(); i++) {
    //         for(auto &e : flow.graph[i]) {
    //             if(!e.isrev && ~e.idx) ans[e.idx] = up[e.idx] - e.cap;
    //         }
    //     }
    //     for(auto &p : ans) cout << p << endl;
    // }
};

int main() {
    int n, m;
    while(cin >> n >> m, n) {
        vi u(m), v(m);
        rep(i, m) {
            cin >> u[i] >> v[i];
            --u[i], --v[i];
        }
        auto check = [&](int l, int r) {
            lrFlow<int> flow(n + m + 2);
            int s = n + m, t = n + m + 1;
            rep(i, m) {
                flow.add_edge(s, i, 1, 1);
                flow.add_edge(i, m + u[i], 0, 1);
                flow.add_edge(i, m + v[i], 0, 1);
            }
            rep(i, n) { flow.add_edge(m + i, t, l, r); }
            auto res = flow.max_flow(s, t) == m;
            return res;
        };
        int p = 0, q = n;
        int l = 0;
        rep(r, n + 1) {
            while(l <= r and check(l, r)) {
                p = l, q = r;
                ++l;
            }
        }
        cout << p << " " << q << endl;
    }
}
accuracy: 1
time_ms: 350
memory_kb: 4412
final_rank: 5

code_snippet: #include<bits/stdc++.h>
// #include<atcoder/all>
// #include<boost/multiprecision/cpp_int.hpp>

using namespace std;
// using namespace atcoder;
// using bint = boost::multiprecision::cpp_int;
using ll = long long;
using ull = unsigned long long;
using ld = long double;
using P = pair<ll,ll>;
using vi = vector<ll>;
using vvi = vector<vi>;
using vvvi = vector<vvi>;
using ve = vector<vector<int>>;
using vb = vector<bool>;
using vvb = vector<vb>;
#define rep(i,n) for(ll i = 0;i < (ll)n;i++)
#define ALL(x) (x).begin(),(x).end()
#define sz(c) ((ll)(c).size())
#define LB(A,x) (int)(lower_bound(A.begin(),A.end(),x)-A.begin())
#define UB(A,x) (int)(upper_bound(A.begin(),A.end(),x)-A.begin())
// #define MOD 1000000007
#define MOD 998244353
template<typename T>using min_priority_queue=priority_queue<T,vector<T>,greater<T>>;
template<typename T>ostream&operator<<(ostream&os,vector<T>&v){for(int i = 0;i < v.size();i++)os<<v[i]<<(i+1!=v.size()?" ":"");return os;}
template<typename T>istream&operator>>(istream&is,vector<T>&v){for(T&in:v)is>>in;return is;}
template<typename T1,typename T2>ostream&operator<<(ostream&os,pair<T1,T2>&p){os<<p.first<<" "<<p.second;return os;}
template<typename T1,typename T2>istream&operator>>(istream&is,pair<T1,T2>&p){is>>p.first>>p.second;return is;}
template<typename T> inline bool chmax(T &a,T b){if(a < b){a = b;return true;}return false;}
template<typename T> inline bool chmin(T &a,T b){if(a > b){a = b;return true;}return false;}
ld dist(ld x1,ld y1,ld x2, ld y2){return sqrtl((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));}


// https://ei1333.github.io/luzhiled/snippets/graph/dinic.html
template<typename flow_t>
struct Dinic{
  /*
    復元をするときはfromの頂点から出る辺からのcapを出力．
    is_e == trueの時その辺は元のグラフにあった"本当の"辺．
    "本当の"辺eについてfr -> toにv[e.to][e.ref].capだけフローが流れている．
    二部マッチングの復元の場合，仮想の始点（終点）から（へ）の辺に注意．
  */
  struct edge{
    int to;
    flow_t cap;
    int ref;//逆辺の番号
    bool is_e;//本物の辺かどうか
    int idx;//入力で与えられる辺としてのindex
  };
  vector<vector<edge>> v;
  vector<int> mn_cost,iter;
  const flow_t inf;
  Dinic(int n):inf(numeric_limits<flow_t>::max()),v(n){}
  void add_edge(int fr,int to,flow_t cap,int idx = -1){
    v[fr].emplace_back((edge){to,cap,(int)v[to].size(),true,idx});
    v[to].emplace_back((edge){fr,0,(int)v[fr].size()-1,false,idx});
  }
  bool bfs(int s,int t){
    mn_cost.assign(v.size(),-1);
    queue<int> que;
    mn_cost[s] = 0;
    que.push(s);
    while(!que.empty() && mn_cost[t] == -1){
      int ov = que.front();que.pop();
      for(auto &e : v[ov]){
        if(e.cap > 0 && mn_cost[e.to] == -1){
          mn_cost[e.to] = mn_cost[ov] + 1;
          que.push(e.to);
        }
      }
    }
    return mn_cost[t] != -1;
  }
  flow_t dfs(int ov,const int t,flow_t flow){
    if(ov == t)return flow;
    for(int &i = iter[ov];i < v[ov].size();i++){
      edge &e = v[ov][i];
      if(e.cap > 0 && mn_cost[ov] < mn_cost[e.to]){
        flow_t d = dfs(e.to,t,min(flow,e.cap));
        if(d > 0){
          e.cap -= d;
          v[e.to][e.ref].cap += d;
          return d;
        }
      }
    }
    return 0;
  }
  // O(m n^2)(n:頂点数 m:辺数)
  flow_t max_flow(int s,int t){
    flow_t flow = 0;
    while(bfs(s,t)){
      iter.assign(v.size(),0);
      flow_t f = 0;
      while((f = dfs(s,t,inf)) > 0)flow += f;
    }
    return flow;
  }
  void output(){
    for(int i = 0;i < v.size();i++){
      for(auto &e : v[i]) {
        if(!e.is_e) continue;
        auto &rev_e = v[e.to][e.ref];
        cout << i << "->" << e.to << " (flow: " << rev_e.cap << "/" << e.cap + rev_e.cap << ")" << "\n";
      }
    }
  }
};

int main(){
  
  ios_base::sync_with_stdio(0), cin.tie(0);
  while(1){
    int n,m;cin >> n >> m;
    if(!n)break;
    P res = {0,n+100};
    vector<P> v(m);cin >> v;
    for(auto &[a,b] : v)a--,b--;
    int r = 1;
    rep(l,n){
      if(r < l)r = l;
      while(1){
        if(r >= n)break;
        Dinic<ll> dn(4 + n + m);
        ll sm = 0;
        const int s = n+m,t = n+m+1,S = n+m+2,T = n+m+3;
        rep(i,m){
          auto [a,b] = v[i];
          dn.add_edge(a,n+i,1);
          dn.add_edge(b,n+i,1);
          dn.add_edge(n+i,t,1);
        }
        rep(i,n){
          if(l != r)dn.add_edge(s,i,r-l);
          if(l){
            dn.add_edge(S,i,l);
            dn.add_edge(s,T,l);
            sm += l;
          }
        }
        ll a = dn.max_flow(S,T);
        ll b = dn.max_flow(s,T);
        ll c = dn.max_flow(S,t);
        ll d = dn.max_flow(s,t);
        // cout << b + d << " a\n";
        // cout << a << " " << b << " " << c << " " << d << " : " << sm << " a\n";
        if(sm == a + c && sm == a + b && m == b + d){
          auto [L,R] = res;
          if(r-l < R-L)res = {l,r};
          break;
        }
        r++;
      }
    }
    cout << res << "\n";
  }
  

  return 0;
}
accuracy: 1
time_ms: 310
memory_kb: 4656
final_rank: 11

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

using namespace std;
using ll = long long;
using pll = pair<ll, ll>;
#define rep(i, s, t) for (ll i = s; i < (ll)(t); i++)
#define rrep(i, s, t) for (ll i = (ll)(t) - 1; i >= (ll)(s); i--)
#define all(x) begin(x), end(x)

#define TT template <typename T>
TT using vec = vector<T>;
TT using vvec = vec<vec<T>>;
TT using vvvec = vec<vvec<T>>;
TT using minheap = priority_queue<T, vector<T>, greater<T>>;
TT using maxheap = priority_queue<T>;
template <class T1, class T2> bool chmin(T1 &x, T2 y) {
    return x > y ? (x = y, true) : false;
}
template <class T1, class T2> bool chmax(T1 &x, T2 y) {
    return x < y ? (x = y, true) : false;
}
TT int pre_id(vec<T> &A, T x) {  // must sorted
    return lower_bound(all(A), x) - A.begin() - 1;
}
TT int nex_id(vec<T> &A, T x) {  // must sorted
    return upper_bound(all(A), x) - A.begin();
}
struct io_setup {
    io_setup() {
        ios::sync_with_stdio(false);
        std::cin.tie(nullptr);
        cout << fixed << setprecision(15);
    }
} io_setup;

template <class T> struct mf_graph {
    struct edge {
        int st, to;
        T cap, flow;
    };

    struct nedge {
        int to, rev;
        T cap;
    };

    int n;
    vec<unordered_map<int, int>> rev;
    vec<pair<int, int>> pos;
    vec<vec<nedge>> g;

    mf_graph(int _n) : n(_n), g(n), rev(n) {}

    int add_edge(int s, int t, T cap) {
        int m = pos.size();
        pos.push_back({s, g[s].size()});
        int fi = g[s].size();
        int ti = g[t].size();
        if (s == t) ti++;
        g[s].push_back(nedge{t, ti, cap});
        g[t].push_back(nedge{s, fi, 0});
        rev[s][t] = m;
        return m;
    }

    T flow(int s, int t, T flow_limit = numeric_limits<T>::max()) {
        vec<int> lv(n), it(n, 0);

        auto bfs = [&]() {
            queue<int> que;
            fill(lv.begin(), lv.end(), -1);
            lv[s] = 0;
            que.push(s);
            while (!que.empty()) {
                int v = que.front();
                que.pop();

                for (auto e : g[v]) {
                    if (e.cap == 0 || lv[e.to] >= 0) continue;
                    lv[e.to] = lv[v] + 1;
                    if (e.to == t) return;
                    que.push(e.to);
                }
            }
        };

        auto dfs = [&](auto f, int v, T up) {
            if (v == s) return up;
            T res = 0;
            int LV = lv[v];
            for (int &i = it[v]; i < int(g[v].size()); i++) {
                nedge &e = g[v][i];
                if (LV <= lv[e.to] || g[e.to][e.rev].cap == 0) continue;
                T d = f(f, 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;
            }
            lv[v] = n;
            return res;
        };

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

    // 以下、不要なら省略

    int get_id(int s, int t) { return rev[s][t]; }

    edge get_edge(int i) {
        int m = pos.size();
        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};
    }

    void change_edge(int i, T nc, T nf) {
        int m = pos.size();
        auto &e = g[pos[i].first][pos[i].second];
        auto &re = g[e.to][e.rev];
        e.cap = nc - nf;
        re.cap = nf;
    }

    vec<bool> min_cut(int s) {
        vec<bool> seen(n);
        queue<int> que;
        que.push(s);
        while (!que.empty()) {
            int p = que.front();
            que.pop();
            seen[p] = true;
            for (auto e : g[p]) {
                if (e.cap && !seen[e.to]) {
                    seen[e.to] = true;
                    que.push(e.to);
                }
            }
        }
        return seen;
    }
};

/*
@brief Maxflow
@docs doc/maxflow.md
*/

struct mf_graph_with_lowerbound {
    int n;
    mf_graph<ll> mf;
    int S, T;
    ll sum;
    vector<ll> lows, d;
    ll total_flow;

    mf_graph_with_lowerbound(int n)
        : n(n), mf(n + 2), S(n), T(n + 1), sum(0), d(n, 0), total_flow(0) {}

    int add_edge(int from, int to, ll low, ll high) {
        lows.push_back(low);
        d[from] -= low;
        d[to] += low;
        return mf.add_edge(from, to, high - low);
    }

    void build() {
        rep(i, 0, n) {
            if (d[i] > 0) {
                mf.add_edge(S, i, d[i]);
                sum += d[i];
            } else if(d[i] < 0) {
                mf.add_edge(i, T, -d[i]);
            }
        }
    }

    bool is_feasable(int s, int t) {
        build();
        mf.add_edge(t, s, LLONG_MAX / 4);  // INF = LLONG_MAX / 4
        total_flow += mf.flow(S, T);
        return total_flow == sum;
    }

    ll flow(int s, int t) {
        if (is_feasable(s, t) == false) return -1;
        mf.add_edge(S, s, LLONG_MAX / 4);
        mf.add_edge(t, T, LLONG_MAX / 4);
        return (total_flow += mf.flow(S, T)) - sum;
    }

    vector<ll> output() {
        vector<ll> res;
        rep(i, 0, lows.size()) { res.push_back(lows[i] + mf.get_edge(i).flow); }
        return res;
    }
};

int main() {
    int n, m;
    while (1) {
        cin >> n >> m;

        if (n == 0) break;

        vector<pll> es;
        rep(i, 0, m) {
            int u, v;
            cin >> u >> v;
            u--, v--;
            es.emplace_back(u, v);
        }

        int ans_m = -1;
        int ans_M = -1;

        auto che = [&](int mi, int Mi) {
            int s = n + m;
            int t = s + 1;

            mf_graph_with_lowerbound mf(t + 1);

            rep(i, 0, m) {
                mf.add_edge(s, i, 1, 1);
                mf.add_edge(i, m + es[i].first, 0, 1);
                mf.add_edge(i, m + es[i].second, 0, 1);
            }

            rep(i, 0, n) { mf.add_edge(m + i, t, mi, Mi); }

            if (mf.is_feasable(s, t)) {
                return true;
            } else
                return false;
        };

        ll w = 1e7;
        rrep(mi, 0, n + 1) {
            ll li = 0;
            ll ri = li + min<ll>(w, m);
            while (li < ri) {  /// xxxxoooo
                ll mid = (li + ri) / 2;
                if (che(mi, mi + mid)) {
                    ri = mid;
                } else {
                    li = mid + 1;
                }
            }
            if (che(mi, mi + li) && chmin(w, li - mi)) {
                ans_m = mi;
                ans_M = mi + li;
            }
        }

        cout << ans_m << " " << ans_M << endl;
    }
}
accuracy: 1
time_ms: 4540
memory_kb: 5392
final_rank: 19

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

using namespace std;
using ll = long long;
using pll = pair<ll, ll>;
#define rep(i, s, t) for (ll i = s; i < (ll)(t); i++)
#define rrep(i, s, t) for (ll i = (ll)(t) - 1; i >= (ll)(s); i--)
#define all(x) begin(x), end(x)

#define TT template <typename T>
TT using vec = vector<T>;
TT using vvec = vec<vec<T>>;
TT using vvvec = vec<vvec<T>>;
TT using minheap = priority_queue<T, vector<T>, greater<T>>;
TT using maxheap = priority_queue<T>;
template <class T1, class T2> bool chmin(T1 &x, T2 y) {
    return x > y ? (x = y, true) : false;
}
template <class T1, class T2> bool chmax(T1 &x, T2 y) {
    return x < y ? (x = y, true) : false;
}
TT int pre_id(vec<T> &A, T x) {  // must sorted
    return lower_bound(all(A), x) - A.begin() - 1;
}
TT int nex_id(vec<T> &A, T x) {  // must sorted
    return upper_bound(all(A), x) - A.begin();
}
struct io_setup {
    io_setup() {
        ios::sync_with_stdio(false);
        std::cin.tie(nullptr);
        cout << fixed << setprecision(15);
    }
} io_setup;

template <class T> struct mf_graph {
    struct edge {
        int st, to;
        T cap, flow;
    };

    struct nedge {
        int to, rev;
        T cap;
    };

    int n;
    vec<unordered_map<int, int>> rev;
    vec<pair<int, int>> pos;
    vec<vec<nedge>> g;

    mf_graph(int _n) : n(_n), g(n), rev(n) {}

    int add_edge(int s, int t, T cap) {
        int m = pos.size();
        pos.push_back({s, g[s].size()});
        int fi = g[s].size();
        int ti = g[t].size();
        if (s == t) ti++;
        g[s].push_back(nedge{t, ti, cap});
        g[t].push_back(nedge{s, fi, 0});
        rev[s][t] = m;
        return m;
    }

    T flow(int s, int t, T flow_limit = numeric_limits<T>::max()) {
        vec<int> lv(n), it(n, 0);

        auto bfs = [&]() {
            queue<int> que;
            fill(lv.begin(), lv.end(), -1);
            lv[s] = 0;
            que.push(s);
            while (!que.empty()) {
                int v = que.front();
                que.pop();

                for (auto e : g[v]) {
                    if (e.cap == 0 || lv[e.to] >= 0) continue;
                    lv[e.to] = lv[v] + 1;
                    if (e.to == t) return;
                    que.push(e.to);
                }
            }
        };

        auto dfs = [&](auto f, int v, T up) {
            if (v == s) return up;
            T res = 0;
            int LV = lv[v];
            for (int &i = it[v]; i < int(g[v].size()); i++) {
                nedge &e = g[v][i];
                if (LV <= lv[e.to] || g[e.to][e.rev].cap == 0) continue;
                T d = f(f, 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;
            }
            lv[v] = n;
            return res;
        };

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

    // 以下、不要なら省略

    int get_id(int s, int t) { return rev[s][t]; }

    edge get_edge(int i) {
        int m = pos.size();
        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};
    }

    void change_edge(int i, T nc, T nf) {
        int m = pos.size();
        auto &e = g[pos[i].first][pos[i].second];
        auto &re = g[e.to][e.rev];
        e.cap = nc - nf;
        re.cap = nf;
    }

    vec<bool> min_cut(int s) {
        vec<bool> seen(n);
        queue<int> que;
        que.push(s);
        while (!que.empty()) {
            int p = que.front();
            que.pop();
            seen[p] = true;
            for (auto e : g[p]) {
                if (e.cap && !seen[e.to]) {
                    seen[e.to] = true;
                    que.push(e.to);
                }
            }
        }
        return seen;
    }
};

/*
@brief Maxflow
@docs doc/maxflow.md
*/

struct mf_graph_with_lowerbound {
    int n;
    mf_graph<ll> mf;
    int S, T;
    ll sum;
    vector<ll> lows, d;
    ll total_flow;

    mf_graph_with_lowerbound(int n)
        : n(n), mf(n + 2), S(n), T(n + 1), sum(0), d(n, 0), total_flow(0) {}

    int add_edge(int from, int to, ll low, ll high) {
        lows.push_back(low);
        d[from] -= low;
        d[to] += low;
        return mf.add_edge(from, to, high - low);
    }

    void build() {
        rep(i, 0, n) {
            if (d[i] > 0) {
                mf.add_edge(S, i, d[i]);
                sum += d[i];
            } else
                mf.add_edge(i, T, -d[i]);
        }
    }

    bool is_feasable(int s, int t) {
        build();
        mf.add_edge(t, s, LLONG_MAX / 4);  // INF = LLONG_MAX / 4
        total_flow += mf.flow(S, T);
        return total_flow == sum;
    }

    ll flow(int s, int t) {
        if (is_feasable(s, t) == false) return -1;
        mf.add_edge(S, s, LLONG_MAX / 4);
        mf.add_edge(t, T, LLONG_MAX / 4);
        return (total_flow += mf.flow(S, T)) - sum;
    }

    vector<ll> output() {
        vector<ll> res;
        rep(i, 0, lows.size()) { res.push_back(lows[i] + mf.get_edge(i).flow); }
        return res;
    }
};

int main() {
    int n, m;
    while (1) {
        cin >> n >> m;

        if (n == 0) break;

        vector<pll> es;
        rep(i, 0, m) {
            int u, v;
            cin >> u >> v;
            u--, v--;
            es.emplace_back(u, v);
        }

        int ans_m = -1;
        int ans_M = -1;

        auto che = [&](int mi, int Mi) {
            int s = n + m;
            int t = s + 1;

            mf_graph_with_lowerbound mf(t + 1);

            rep(i, 0, m) {
                mf.add_edge(s, i, 1, 1);
                mf.add_edge(i, m + es[i].first, 0, 1);
                mf.add_edge(i, m + es[i].second, 0, 1);
            }

            rep(i, 0, n) { mf.add_edge(m + i, t, mi, Mi); }

            if (mf.is_feasable(s, t)) {
                return true;
            } else
                return false;
        };

        ll w = 1e7;
        rrep(mi, 0, n + 1) {
            ll li = 0;
            ll ri = li + min<ll>(w, m);
            while (li < ri) {  /// xxxxoooo
                ll mid = (li + ri) / 2;
                if (che(mi, mi + mid)) {
                    ri = mid;
                } else {
                    li = mid + 1;
                }
            }
            if (che(mi, mi + li) && chmin(w, li - mi)) {
                ans_m = mi;
                ans_M = mi + li;
            }
        }

        cout << ans_m << " " << ans_M << endl;
    }
}
accuracy: 1
time_ms: 4540
memory_kb: 5316
final_rank: 18

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

using namespace std;
using ll = long long;
using pll = pair<ll, ll>;
#define rep(i, s, t) for (ll i = s; i < (ll)(t); i++)
#define rrep(i, s, t) for (ll i = (ll)(t) - 1; i >= (ll)(s); i--)
#define all(x) begin(x), end(x)

#define TT template <typename T>
TT using vec = vector<T>;
TT using vvec = vec<vec<T>>;
TT using vvvec = vec<vvec<T>>;
TT using minheap = priority_queue<T, vector<T>, greater<T>>;
TT using maxheap = priority_queue<T>;
template <class T1, class T2> bool chmin(T1 &x, T2 y) {
    return x > y ? (x = y, true) : false;
}
template <class T1, class T2> bool chmax(T1 &x, T2 y) {
    return x < y ? (x = y, true) : false;
}
TT int pre_id(vec<T> &A, T x) {  // must sorted
    return lower_bound(all(A), x) - A.begin() - 1;
}
TT int nex_id(vec<T> &A, T x) {  // must sorted
    return upper_bound(all(A), x) - A.begin();
}
struct io_setup {
    io_setup() {
        ios::sync_with_stdio(false);
        std::cin.tie(nullptr);
        cout << fixed << setprecision(15);
    }
} io_setup;


template<class T> 
struct mf_graph {
    struct edge {
        int st, to;
        T cap, flow;
    };

    struct nedge {
        int to, rev;
        T cap;
    };

    int n;
    vec<unordered_map<int, int>> rev;
    vec<pair<int, int>> pos;
    vec<vec<nedge>> g;

    mf_graph(int _n) : n(_n), g(n), rev(n) {}

    int add_edge(int s, int t, T cap) {
        int m = pos.size();
        pos.push_back({s, g[s].size()});
        int fi = g[s].size(); int ti = g[t].size();
        if(s==t)ti++;
        g[s].push_back(nedge{t, ti, cap});
        g[t].push_back(nedge{s, fi, 0});
        rev[s][t] = m;
        return m;
    }

    T flow(int s, int t, T flow_limit = numeric_limits<T>::max()) {
        vec<int> lv(n), it(n, 0);

        auto bfs = [&]() {
            queue<int> que;
            fill(lv.begin(), lv.end(), -1);
            lv[s] = 0;
            que.push(s);
            while(!que.empty()) {
                int v = que.front();
                que.pop();

                for(auto e : g[v]) {
                    if(e.cap == 0 || lv[e.to] >= 0) continue;
                    lv[e.to] = lv[v] + 1;
                    if(e.to == t) return;
                    que.push(e.to);
                }
            }
        };

        auto dfs = [&](auto f, int v, T up) {
            if(v==s) return up;
            T res = 0;
            int LV = lv[v];
            for(int& i = it[v]; i < int(g[v].size()); i++) {
                nedge& e = g[v][i];
                if(LV <= lv[e.to] || g[e.to][e.rev].cap == 0) continue;
                T d = f(f, 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;
            }
            lv[v] = n;
            return res;
        };

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

    //以下、不要なら省略

    int get_id(int s, int t) {
        return rev[s][t];
    }

    edge get_edge(int i) {
        int m = pos.size();
        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};
    }

    void change_edge(int i, T nc, T nf) {
        int m = pos.size();
        auto& e = g[pos[i].first][pos[i].second];
        auto& re = g[e.to][e.rev];
        e.cap = nc - nf;
        re.cap = nf;
    }

    vec<bool> min_cut(int s) {
        vec<bool> seen(n);
        queue<int> que;
        que.push(s);
        while(!que.empty()) {
            int p = que.front();
            que.pop();
            seen[p]=true;
            for(auto e : g[p]) {
                if(e.cap && !seen[e.to]) {
                    seen[e.to] = true;
                    que.push(e.to);
                }
            }
        }
        return seen;
    }

};



/*
@brief Maxflow
@docs doc/maxflow.md
*/

struct mf_graph_with_lowerbound {
    int n;
    mf_graph<ll> mf;
    int S, T;
    ll sum;
    vector<ll> lows, d;
    ll total_flow;


    mf_graph_with_lowerbound(int n) : n(n), mf(n+ 2), S(n), T(n + 1), sum(0), d(n, 0), total_flow(0) {
    }


    int add_edge(int from, int to, ll low, ll high) {
        lows.push_back(low);
        d[from] -= low;
        d[to] += low;
        return mf.add_edge(from, to, high - low);
    }

    void build() {
        rep(i, 0, n) {
            if(d[i] > 0) {
                mf.add_edge(S, i, d[i]);
                sum += d[i];
            }
            else mf.add_edge(i, T, -d[i]);
        }
    }

    bool is_feasable(int s, int t) {
        build();
        mf.add_edge(t, s, LLONG_MAX / 4); // INF = LLONG_MAX / 4
        total_flow += mf.flow(S, T);
        return total_flow == sum;
    }

    ll flow(int s, int t) {
        if(is_feasable(s, t) == false) return -1;
        mf.add_edge(S, s, LLONG_MAX / 4);
        mf.add_edge(t, T, LLONG_MAX / 4);
        return (total_flow += mf.flow(S, T)) - sum;
    }

    vector<ll> output() {
        vector<ll> res;
        rep(i, 0, lows.size()) {
            res.push_back(lows[i] + mf.get_edge(i).flow);
        }
        return res;

    }
};


int main() {
     int n, m;
     while(1) {
    cin >> n >> m;

    if(n == 0) break;

    vector<pll> es;
    rep(i, 0, m) {
        int u, v;
        cin >> u >> v;
        u--, v--;
        es.emplace_back(u, v);
    }


    int ans_m = -1;
    int ans_M = -1;

    auto che = [&](int mi, int Mi) {
        int s = n + m;
            int t = s + 1;

            mf_graph_with_lowerbound mf(t + 1);
    

           rep(i, 0, m) {
            mf.add_edge(s, i, 1, 1);
            mf.add_edge(i, m + es[i].first, 0, 1);
            mf.add_edge(i, m + es[i].second, 0, 1);
           }

           rep(i, 0, n) {
            mf.add_edge(m + i, t, mi, Mi);
           }

            if(mf.is_feasable(s, t)) {
                return true;
            }
            else return false;

    };

    
    ll w = 1e7;
    rrep(mi, 0, n + 1) {
        ll li = 0;
        ll ri = m;
        while(li < ri) {///xxxxoooo
            ll mid = (li + ri) / 2;
            if(che(mi, mi + mid)) {
                ri = mid;
            }
            else {
                li = mid + 1;
            }
        }
        if(che(mi, mi + li) && chmin(w, li - mi)) {
            ans_m = mi;
            ans_M = mi + li;

        }
    }

    cout << ans_m << " " << ans_M << endl;
    }
   

}
accuracy: 1
time_ms: 5360
memory_kb: 5344
final_rank: 20

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...);
}

#include <unistd.h>

namespace fastio {
static constexpr uint32_t SZ = 1 << 17;
char ibuf[SZ];
char obuf[SZ];
char out[100];
// pointer of ibuf, obuf


uint32_t pil = 0, pir = 0, por = 0;

struct Pre {
    char num[10000][4];
    constexpr Pre() : num() {
        for (int i = 0; i < 10000; i++) {
            int n = i;
            for (int j = 3; j >= 0; j--) {
                num[i][j] = n % 10 | '0';
                n /= 10;
            }
        }
    }
} constexpr pre;

inline void load() {
    memmove(ibuf, ibuf + pil, pir - pil);
    pir = pir - pil + fread(ibuf + pir - pil, 1, SZ - pir + pil, stdin);
    pil = 0;
    if (pir < SZ)
        ibuf[pir++] = '\n';
}

inline void flush() {
    fwrite(obuf, 1, por, stdout);
    por = 0;
}

void rd(char &c) {
    do {
        if (pil + 1 > pir)
            load();
        c = ibuf[pil++];
    } while (isspace(c));
}

void rd(string &x) {
    x.clear();
    char c;
    do {
        if (pil + 1 > pir)
            load();
        c = ibuf[pil++];
    } while (isspace(c));
    do {
        x += c;
        if (pil == pir)
            load();
        c = ibuf[pil++];
    } while (!isspace(c));
}

template <typename T> void rd_real(T &x) {
    string s;
    rd(s);
    x = stod(s);
}

template <typename T> void rd_integer(T &x) {
    if (pil + 100 > pir)
        load();
    char c;
    do
        c = ibuf[pil++];
    while (c < '-');
    bool minus = 0;
    if constexpr (is_signed<T>::value || is_same_v<T, i128>) {
        if (c == '-') {
            minus = 1, c = ibuf[pil++];
        }
    }
    x = 0;
    while ('0' <= c) {
        x = x * 10 + (c & 15), c = ibuf[pil++];
    }
    if constexpr (is_signed<T>::value || is_same_v<T, i128>) {
        if (minus)
            x = -x;
    }
}

void rd(int &x) {
    rd_integer(x);
}
void rd(ll &x) {
    rd_integer(x);
}
void rd(i128 &x) {
    rd_integer(x);
}
void rd(uint &x) {
    rd_integer(x);
}
void rd(ull &x) {
    rd_integer(x);
}
void rd(u128 &x) {
    rd_integer(x);
}
void rd(double &x) {
    rd_real(x);
}
void rd(long double &x) {
    rd_real(x);
}

template <class T, class U> void rd(pair<T, U> &p) {
    return rd(p.first), rd(p.second);
}
template <size_t N = 0, typename T> void rd_tuple(T &t) {
    if constexpr (N < std::tuple_size<T>::value) {
        auto &x = std::get<N>(t);
        rd(x);
        rd_tuple<N + 1>(t);
    }
}
template <class... T> void rd(tuple<T...> &tpl) {
    rd_tuple(tpl);
}

template <size_t N = 0, typename T> void rd(array<T, N> &x) {
    for (auto &d : x)
        rd(d);
}
template <class T> void rd(vector<T> &x) {
    for (auto &d : x)
        rd(d);
}

void read() {}
template <class H, class... T> void read(H &h, T &...t) {
    rd(h), read(t...);
}

void wt(const char c) {
    if (por == SZ)
        flush();
    obuf[por++] = c;
}
void wt(const string s) {
    for (char c : s)
        wt(c);
}
void wt(const char *s) {
    size_t len = strlen(s);
    for (size_t i = 0; i < len; i++)
        wt(s[i]);
}

template <typename T> void wt_integer(T x) {
    if (por > SZ - 100)
        flush();
    if (x < 0) {
        obuf[por++] = '-', x = -x;
    }
    int outi;
    for (outi = 96; x >= 10000; outi -= 4) {
        memcpy(out + outi, pre.num[x % 10000], 4);
        x /= 10000;
    }
    if (x >= 1000) {
        memcpy(obuf + por, pre.num[x], 4);
        por += 4;
    } else if (x >= 100) {
        memcpy(obuf + por, pre.num[x] + 1, 3);
        por += 3;
    } else if (x >= 10) {
        int q = (x * 103) >> 10;
        obuf[por] = q | '0';
        obuf[por + 1] = (x - q * 10) | '0';
        por += 2;
    } else
        obuf[por++] = x | '0';
    memcpy(obuf + por, out + outi + 4, 96 - outi);
    por += 96 - outi;
}

template <typename T> void wt_real(T x) {
    ostringstream oss;
    oss << fixed << setprecision(15) << double(x);
    string s = oss.str();
    wt(s);
}

void wt(int x) {
    wt_integer(x);
}
void wt(ll x) {
    wt_integer(x);
}
void wt(i128 x) {
    wt_integer(x);
}
void wt(uint x) {
    wt_integer(x);
}
void wt(ull x) {
    wt_integer(x);
}
void wt(u128 x) {
    wt_integer(x);
}
void wt(double x) {
    wt_real(x);
}
void wt(long double x) {
    wt_real(x);
}

template <class T, class U> void wt(const pair<T, U> val) {
    wt(val.first);
    wt(' ');
    wt(val.second);
}
template <size_t N = 0, typename T> void wt_tuple(const T t) {
    if constexpr (N < std::tuple_size<T>::value) {
        if constexpr (N > 0) {
            wt(' ');
        }
        const auto x = std::get<N>(t);
        wt(x);
        wt_tuple<N + 1>(t);
    }
}
template <class... T> void wt(tuple<T...> tpl) {
    wt_tuple(tpl);
}
template <class T, size_t S> void wt(const array<T, S> val) {
    auto n = val.size();
    for (size_t i = 0; i < n; i++) {
        if (i)
            wt(' ');
        wt(val[i]);
    }
}
template <class T> void wt(const vector<T> val) {
    auto n = val.size();
    for (size_t i = 0; i < n; i++) {
        if (i)
            wt(' ');
        wt(val[i]);
    }
}

void print() {
    wt('\n');
}
template <class Head, class... Tail> void print(Head &&head, Tail &&...tail) {
    wt(head);
    if (sizeof...(Tail))
        wt(' ');
    print(forward<Tail>(tail)...);
}
void __attribute__((destructor)) _d() {
    flush();
}
} // namespace fastio


using fastio::flush;
using fastio::print;
using fastio::read;

inline void first(bool i = true) {
    print(i ? "first" : "second");
}
inline void Alice(bool i = true) {
    print(i ? "Alice" : "Bob");
}
inline void Takahashi(bool i = true) {
    print(i ? "Takahashi" : "Aoki");
}
inline void yes(bool i = true) {
    print(i ? "yes" : "no");
}
inline void Yes(bool i = true) {
    print(i ? "Yes" : "No");
}
inline void No() {
    print("No");
}
inline void YES(bool i = true) {
    print(i ? "YES" : "NO");
}
inline void NO() {
    print("NO");
}
inline void Yay(bool i = true) {
    print(i ? "Yay!" : ":(");
}
inline void Possible(bool i = true) {
    print(i ? "Possible" : "Impossible");
}
inline void POSSIBLE(bool i = true) {
    print(i ? "POSSIBLE" : "IMPOSSIBLE");
}

/**
 * @brief Fast IO
 */

struct MaxFlow {
    struct Edge {
        int to, rev;
        ll cap;
    };
    int V;
    vector<vector<Edge>> G;
    vector<int> itr, level;
    using P = pair<int, int>;
    vector<P> es;

  public:
    MaxFlow() {}
    MaxFlow(int V) : V(V) {
        G.assign(V, vector<Edge>());
    }
    int add_vertex() {
        G.push_back(vector<Edge>());
        return V++;
    }
    void add_edge(int from, int to, ll cap) {
        int fid = SZ(G[from]), tid = SZ(G[to]);
        if (from == to)
            tid++;
        es.push_back({from, fid});
        G[from].push_back({to, tid, cap});
        G[to].push_back({from, fid, 0});
    }
    struct Type {
        int from, to;
        ll cap, recap;
    };
    Type get_edge(int i) {
        auto [from, pos] = es[i];
        auto e = G[from][pos];
        auto re = G[e.to][e.rev];
        return Type{from, e.to, e.cap, re.cap};
    }
    void bfs(int s) {
        level.assign(V, -1);
        queue<int> q;
        level[s] = 0;
        q.push(s);
        while (!q.empty()) {
            int v = q.front();
            q.pop();
            for (auto &e : G[v]) {
                if (e.cap > 0 && level[e.to] < 0) {
                    level[e.to] = level[v] + 1;
                    q.push(e.to);
                }
            }
        }
    }
    ll dfs(int v, int t, ll f) {
        if (v == t)
            return f;
        for (int &i = itr[v]; i < (int)G[v].size(); i++) {
            Edge &e = G[v][i];
            if (e.cap > 0 && level[v] < level[e.to]) {
                ll d = dfs(e.to, t, min(f, e.cap));
                if (d > 0) {
                    e.cap -= d, G[e.to][e.rev].cap += d;
                    return d;
                }
            }
        }
        return 0;
    }
    ll run(int s, int t) {
        ll ret = 0, f;
        while (bfs(s), level[t] >= 0) {
            itr.assign(V, 0);
            while ((f = dfs(s, t, INF)) > 0)
                ret += f;
        }
        return ret;
    }
    vector<int> cut() {
        vector<int> ret(V);
        rep(v, 0, V) if (level[v] < 0) ret[v] = 1;
        return ret;
    }
};

/**
 * @brief Maximum Flow
 */

template <class Cap, class Cost> struct MinCostFlow {
    struct X {
        int from, to;
        Cap lb, ub, flow;
        Cost cost;
    };
    struct Edge {
        int to, rev;
        Cap cap;
        Cost cost;
    };
    using P = pair<int, int>;
    int n, m;
    vector<X> es;
    vector<Cap> exc;
    vector<Cost> dual;
    vector<vector<Edge>> g;
    Cost MX;
    MinCostFlow(int _n) : n(_n), m(0), exc(n), dual(n), g(n), MX(0) {}
    void add_edge(int from, int to, Cap lb, Cap ub, Cost cost) {
        m++;
        chmax(MX, cost);
        chmax(MX, -cost);
        es.push_back({from, to, lb, ub, 0, cost});
    }
    void add_excess(int v, Cap c) { exc[v] += c; }
    pair<bool, Cost> run() {
        MaxFlow mf(n + 2);
        int S = n, T = n + 1;
        Cap psum = 0, nsum = 0;
        for (auto &e : es) {
            exc[e.to] += e.lb;
            exc[e.from] -= e.lb;
            mf.add_edge(e.from, e.to, e.ub - e.lb);
        }
        rep(i, 0, n) {
            if (exc[i] > 0) {
                psum += exc[i];
                mf.add_edge(S, i, exc[i]);
            }
            if (exc[i] < 0) {
                nsum += -exc[i];
                mf.add_edge(i, T, -exc[i]);
            }
        }

        if (psum != nsum or mf.run(S, T) != psum)
            return {false, 0};

        using P = pair<int, int>;
        vector<P> pos;
        rep(i, 0, m) {
            auto e = mf.get_edge(i);
            Cost cost = es[i].cost * n;
            int fid = SZ(g[e.from]), tid = SZ(g[e.to]);
            if (e.from == e.to)
                tid++;
            pos.push_back({e.from, fid});
            g[e.from].push_back({e.to, tid, (int)e.cap, cost});
            g[e.to].push_back({e.from, fid, (int)e.recap, -cost});
        }

        // solve

        Cost eps = MX * n + 1;
        while (eps > 1) {
            eps = max<Cost>(eps >> 2, 1);
            refine(eps);
        }

        Cost ret = 0;
        rep(i, 0, m) {
            auto [from, fid] = pos[i];
            es[i].flow = es[i].ub - g[from][fid].cap;
            ret += es[i].flow * es[i].cost;
        }
        dual.assign(n, 0);
        for (;;) {
            bool upd = 0;
            rep(i, 0, n) {
                for (auto &e : g[i])
                    if (e.cap) {
                        auto cost = dual[i] + e.cost / n;
                        if (chmin(dual[e.to], cost)) {
                            upd = 1;
                        }
                    }
            }
            if (!upd)
                break;
        }
        return {true, ret};
    }
    Cap get_flow(int i) const { return es[i].flow; }

  private:
    void refine(Cost &eps) {
        exc.assign(n, 0);
        vector<int> used(n);
        queue<int> que;
        vector<int> iter(n);

        auto cost = [&](int from, const Edge &e) {
            return e.cost + dual[from] - dual[e.to];
        };
        auto push = [&](int from, Edge &e, Cap cap) {
            exc[from] -= cap;
            exc[e.to] += cap;
            g[e.to][e.rev].cap += cap;
            e.cap -= cap;
        };
        auto relabel = [&](int v) {
            iter[v] = 0;
            Cost down = MX * (n + 1);
            for (auto &e : g[v])
                if (e.cap) {
                    chmin(down, eps + cost(v, e));
                }
            dual[v] -= down;
            que.push(v);
            used[v] = 1;
        };

        rep(i, 0, n) {
            for (auto &e : g[i])
                if (e.cap and cost(i, e) < 0) {
                    push(i, e, e.cap);
                }
        }
        rep(i, 0, n) if (exc[i] > 0) {
            used[i] = 1;
            que.push(i);
        }
        while (!que.empty()) {
            auto v = que.front();
            que.pop();
            used[v] = 0;
            for (int &i = iter[v]; i < SZ(g[v]); i++) {
                auto &e = g[v][i];
                if (e.cap and cost(v, e) < 0) {
                    push(v, e, min(exc[v], e.cap));
                    if (!used[e.to] and exc[e.to] > 0) {
                        used[e.to] = 1;
                        que.push(e.to);
                    }
                    if (exc[v] == 0)
                        break;
                }
            }
            if (exc[v] > 0) {
                relabel(v);
            }
        }
        eps = 0;
        rep(i, 0, n) {
            for (auto &e : g[i])
                if (e.cap) {
                    chmax(eps, -cost(i, e));
                }
        }
    }
};

/**
 * @brief Minimum Cost b-flow
 */

bool f(const pair<int,int>& P1, const pair<int,int>& P2) {
    if (P1.second-P1.first != P2.second-P2.first) return P1.second-P1.first<P2.second-P2.first;
    return P1.first > P2.first;
}

int main() {
while(1) {
    int N, M;
    cin >> N >> M;
    if (N == 0 && M == 0) return 0;
    vector<int> U(M), V(M);
    rep(i,0,M) cin >> U[i] >> V[i], U[i]--, V[i]--;
    vector<pair<int,int>> Cand;
    for (int L = 0, R = 0; L < N-1; L++) {
        if (L > R) R = L;
        for (; R < N; R++) {
            MinCostFlow<int,int> G(N+M+2);
            rep(i,0,N) {
                G.add_edge(N+M,i,L,R,0);
            }
            rep(i,0,M) {
                G.add_edge(U[i],i+N,0,1,0);
                G.add_edge(V[i],i+N,0,1,0);
                G.add_edge(i+N,N+M+1,0,1,0);
            }
            G.add_edge(N+M+1,N+M,0,M,-1);
            auto [ok, ret] = G.run();
            if (ok) {
                if (ret == -M) {
                    break;
                }
            }
        }
        if (R == N) break;
        Cand.push_back({L,R});
    }
    sort(ALL(Cand),f);
    cout << Cand[0].first << ' ' << Cand[0].second << endl;
}
}
accuracy: 1
time_ms: 1110
memory_kb: 5448
final_rank: 17

code_snippet: #include<bits/stdc++.h>
using namespace std;
using ll = long long;
using vll = vector<ll>;
#define rep(i,n) for(ll i=0;i<n;i++)

void solve(ll N,ll M){
    vector<vll> G(N,vll(N,0));
    vll E(N,0);
    rep(i,M){
        ll U,V;
        cin>>U>>V;
        U--;V--;
        G[U][V]=1;
        E[V]++;    
    }
    while(1){
        ll m=1e18,M=-1e18;
        ll mid=-1,Mid=-1;
        rep(i,N){
            if(E[i]<m){
                m=E[i];
                mid=i;
            }
            if(E[i]>M){
                M=E[i];
                Mid=i;
            }
        }
        if(m==M||m+1==M)break;
        
        bool EX=0;
        queue<ll> Q;
        Q.push(mid);
        vll dep(N,-1);
        dep[mid]=0;
        while(!Q.empty()){
            ll p=Q.front();
            Q.pop();
            rep(i,N){
                if(G[p][i]==1){
                    if(dep[i]!=-1)continue;
                    dep[i]=dep[p]+1;
                    Q.push(i);
                }
            }
        }
        rep(i,N){
            if(dep[i]!=-1&&E[i]>=m+2){
                ll x=i;
                E[i]--;
                E[mid]++;
                while(x!=mid){
                    rep(j,N){
                        if(G[j][x]==1&&dep[j]==dep[x]-1){
                            G[x][j]=1;
                            G[j][x]=0;
                            x=j;
                            break;
                        }
                    }
                }
                EX=1;
                break;
            }
        }
        if(EX)continue;
        
        Q.push(Mid);
        dep.assign(N,-1);
        dep[Mid]=0;
        while(!Q.empty()){
            ll p=Q.front();
            Q.pop();
            rep(i,N){
                if(G[i][p]==1){
                    if(dep[i]!=-1)continue;
                    dep[i]=dep[p]+1;
                    Q.push(i);
                }
            }
        }
        rep(i,N){
            if(dep[i]!=-1&&E[i]<=M-2){
                ll x=i;
                E[i]++;
                E[Mid]--;
                while(x!=Mid){
                    rep(j,N){
                        if(G[x][j]==1&&dep[j]==dep[x]-1){
                            G[x][j]=0;
                            G[j][x]=1;
                            x=j;
                            break;
                        }
                    }
                }
                EX=1;
                break;
            }
        }
        if(EX)continue;

        break;
    }

    sort(E.begin(),E.end());
    cout<<E[0]<<" "<<E.back()<<endl;

}

int main() {

    ll H,W;
    while (cin >> H>>W) {
        if (H+W == 0)return 0;
        solve(H,W);
    }
}
accuracy: 1
time_ms: 90
memory_kb: 3468
final_rank: 1

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 3 "A.cpp"

#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 5 "A.cpp"

pair<int,int> solve(int n, int m) {
    vector<pair<int,int>> e(m);
    for(auto &[u, v] : e) u = in(), v = in(), u--, v--;

    auto f = [&](int L, int R) -> bool {
        mf_graph<int> g(n + m + 4);
        const int S  = n + m + 0, T  = n + m + 1;
        const int SS = n + m + 2, TT = n + m + 3;
        auto V = [&](int i) { return i; };
        auto E = [&](int i) { return n + i; };
        for(int i : rep(m)) {
            auto [u, v] = e[i];
            g.add_edge(V(u), E(i), 1);
            g.add_edge(V(v), E(i), 1);
            g.add_edge(E(i), T, 1);
        }
        for(int v : rep(n)) {
            g.add_edge(S, V(v), R - L);
            g.add_edge(SS, V(v), L);
            g.add_edge(S, TT, L);
        }
        const int SSTT = g.flow(SS, TT);
        const int STT  = g.flow(S , TT);
        const int SST  = g.flow(SS, T );
        const int ST   = g.flow(S , T );
        return STT + ST == m;
    };

    int ansL = 0, ansR = n;
    for(int L = 0, R = 0; R <= n; R++) {
        while(L <= R and f(L, R)) {
            tie(ansL, ansR) = make_pair(L, R);
            L++;
        }
    }
    return {ansL, ansR};
}

int main() {
    while(true) {
        int n = in(), m = in();
        if(make_pair(n, m) == make_pair(0, 0)) return 0;
        auto [l, h] = solve(n, m);
        print(l, h);
    }
}
accuracy: 1
time_ms: 300
memory_kb: 4268
final_rank: 4

code_snippet: #include <iostream>
#include <vector>
#include <algorithm>
#include <map>
#include <queue>
#include <set>
using namespace std;
typedef long long ll;
#define rep(i, n) for(int i = 0; i < (n); i++)

template<class T>
using vi = vector<T>;

template<class T>
using vii = vector<vi<T>>;

template<class T>
using viii = vector<vii<T>>;

struct Dinic {
private:
    const long long inf = 1e18;
    vector<int> dist; //sからの距離
    vector<int> iter; //どこまで調べたか
public:
    struct edge {
        int to, rev; long long cap;
        edge(int t = 0, int r = 0, long long c = 0LL) : to(t), rev(r), cap(c) {}
    };

    int n;
    vector<vector<edge>> to;

    Dinic(int n_ = 1) : n(n_), to(n_), dist(n_), iter(n_) {}

    void addedge(int u, int v, long long cap) {//u->vにcapの容量
        int su = (int)to[u].size(), sv = (int)to[v].size();
        to[u].push_back({ v, sv, cap });
        to[v].push_back({ u, su, 0 });
    }

    long long dfs(int v, int t, long long f) { //t: 終点, f: flow
        if (v == t) return f;
        int sv = (int)to[v].size();
        for (int& i = iter[v]; i < sv; i++) {
            edge& nv = to[v][i];
            if (dist[nv.to] <= dist[v] || nv.cap == 0) continue; //here
            long long nf = dfs(nv.to, t, min(f, nv.cap));
            if (nf > 0) {
                nv.cap -= nf;
                to[nv.to][nv.rev].cap += nf;
                return nf;
            }
        }
        return 0;
    }

    void bfs(int s) {
        dist.assign(n, -1);
        queue<int> q;
        q.push(s);
        dist[s] = 0;
        while (!q.empty()) {
            int v = q.front();
            q.pop();
            for (auto nv : to[v]) {
                if (nv.cap > 0 && dist[nv.to] < 0) {
                    dist[nv.to] = dist[v] + 1;
                    q.push(nv.to);
                }
            }
        }
    }

    long long maxflow(int s, int t) {//s:始点, t:終点
        long long res = 0, flow = 0;
        while (true) {
            bfs(s);
            if (dist[t] < 0) break;

            iter.assign(n, 0);
            while (flow = dfs(s, t, inf)) res += flow;
        };
        return res;
    }
};

int main() 
{
    vi<pair<int, int>> ans;
    int n, m;
    while (cin >> n >> m, n) {
        vi<int> u(m), v(m);
        rep(i, m) cin >> u[i] >> v[i];
        rep(i, m) u[i]--;
        rep(i, m) v[i]--;

        int high = n, low = 0;
        int S = n + m, T = S + 1, T2 = S + 2;

        auto f = [&](int h, int l) {
            Dinic dn(n + m + 3);
            rep(i, m) dn.addedge(S, i, 1);
            rep(i, m) {
                dn.addedge(i, m + u[i], 1);
                dn.addedge(i, m + v[i], 1);
            }
            rep(i, n) {
                dn.addedge(m + i, T, h);
                //dn.addedge(T, m + i, l);
                dn.addedge(m + i, T2, l);
            }
            return dn;
        };

        rep(l, n + 1) {
            for(int h = l; h <= min(n, l + high - low); h++) {
                Dinic dn = f(h, l);
                
                int flow = dn.maxflow(S, T);
                if (flow < m) continue;

                int flow_l = dn.maxflow(T, T2);
                if (flow_l < l * n) continue;

                if (h - l <= high - low) {
                    high = h;
                    low = l;
                    break;
                }
            }
        }
        ans.push_back({ low, high });
    }
    for (auto elem : ans) cout << elem.first << " " << elem.second << endl;
	return 0;
}
accuracy: 1
time_ms: 1810
memory_kb: 4000
final_rank: 7

code_snippet: #include <iostream>
#include <vector>
#include <algorithm>
#include <map>
#include <queue>
#include <set>
using namespace std;
typedef long long ll;
#define rep(i, n) for(int i = 0; i < (n); i++)

template<class T>
using vi = vector<T>;

template<class T>
using vii = vector<vi<T>>;

template<class T>
using viii = vector<vii<T>>;

struct Dinic {
private:
    const long long inf = 1e18;
    vector<int> dist; //sからの距離
    vector<int> iter; //どこまで調べたか
public:
    struct edge {
        int to, rev; long long cap;
        edge(int t = 0, int r = 0, long long c = 0LL) : to(t), rev(r), cap(c) {}
    };

    int n;
    vector<vector<edge>> to;

    Dinic(int n_ = 1) : n(n_), to(n_), dist(n_), iter(n_) {}

    void addedge(int u, int v, long long cap) {//u->vにcapの容量
        int su = (int)to[u].size(), sv = (int)to[v].size();
        to[u].push_back({ v, sv, cap });
        to[v].push_back({ u, su, 0 });
    }

    long long dfs(int v, int t, long long f) { //t: 終点, f: flow
        if (v == t) return f;
        int sv = (int)to[v].size();
        for (int& i = iter[v]; i < sv; i++) {
            edge& nv = to[v][i];
            if (dist[nv.to] <= dist[v] || nv.cap == 0) continue; //here
            long long nf = dfs(nv.to, t, min(f, nv.cap));
            if (nf > 0) {
                nv.cap -= nf;
                to[nv.to][nv.rev].cap += nf;
                return nf;
            }
        }
        return 0;
    }

    void bfs(int s) {
        dist.assign(n, -1);
        queue<int> q;
        q.push(s);
        dist[s] = 0;
        while (!q.empty()) {
            int v = q.front();
            q.pop();
            for (auto nv : to[v]) {
                if (nv.cap > 0 && dist[nv.to] < 0) {
                    dist[nv.to] = dist[v] + 1;
                    q.push(nv.to);
                }
            }
        }
    }

    long long maxflow(int s, int t) {//s:始点, t:終点
        long long res = 0, flow = 0;
        while (true) {
            bfs(s);
            if (dist[t] < 0) break;

            iter.assign(n, 0);
            while (flow = dfs(s, t, inf)) res += flow;
        };
        return res;
    }
};

int main() 
{
    vi<pair<int, int>> ans;
    int n, m;
    while (cin >> n >> m, n) {
        vi<int> u(m), v(m);
        rep(i, m) cin >> u[i] >> v[i];
        rep(i, m) u[i]--;
        rep(i, m) v[i]--;

        int high = n, low = 0;
        int S = n + m, T = S + 1, T2 = S + 2;

        auto f = [&](int h, int l) {
            Dinic dn(n + m + 3);
            rep(i, m) dn.addedge(S, i, 1);
            rep(i, m) {
                dn.addedge(i, m + u[i], 1);
                dn.addedge(i, m + v[i], 1);
            }
            rep(i, n) {
                dn.addedge(m + i, T, h);
                //dn.addedge(T, m + i, l);
                dn.addedge(m + i, T2, l);
            }
            return dn;
        };

        rep(l, n + 1) {
            for(int h = l; h <= min(n, l + high - low); h++) {
                Dinic dn = f(h, l);
                
                int flow = dn.maxflow(S, T);
                if (flow < m) continue;

                int flow_l = dn.maxflow(T, T2);
                if (flow_l < l * n) continue;

                if (h - l <= high - low) {
                    high = h;
                    low = l;
                    break;
                }
            }
        }
        ans.push_back({ low, high });
    }
    for (auto elem : ans) cout << elem.first << " " << elem.second << endl;
	return 0;
}


//
//struct edge {
//    int to, cap, rev;
//};
//
//int main()
//{
//    vi<pair<int, int>> ans;
//    int n, m;
//    while (cin >> n >> m, n) {
//        vii<edge> to(n);
//        vi<int> deg(n);
//        rep(i, m) {
//            int u, v;
//            cin >> u >> v;
//            u--, v--;
//            int su = (int)to[u].size();
//            int sv = (int)to[v].size();
//            to[u].push_back({ v, 0, sv });
//            to[v].push_back({ u, 1, su });
//            deg[u]++;
//        }
//
//        map<int, set<int>> mp;
//        rep(i, n) mp[deg[i]].insert(i);
//
//        vi<bool> visited(n);
//        auto dfs = [&](auto dfs, int s, int v, int mn = 0)->bool {
//            visited[v] = true;
//            if ((!mn && deg[s] + 2 <= deg[v]) 
//                || (mn && deg[v] + 2 <= deg[s])) {
//                mp[deg[v]].erase(v);
//                mp[deg[s]].erase(s);
//                if ((int)mp[deg[v]].size() == 0) mp.erase(deg[v]);
//                if ((int)mp[deg[s]].size() == 0) mp.erase(deg[s]);
//
//                if (!mn) {
//                    deg[v]--;
//                    deg[s]++;
//                }
//                else {
//                    deg[v]++;
//                    deg[s]--;
//                }
//
//                mp[deg[v]].insert(v);
//                mp[deg[s]].insert(s);
//                return true;
//            }
//            for (auto& elem : to[v]) {
//                if (elem.cap == mn || visited[elem.to]) continue;
//                if (dfs(dfs, s, elem.to, mn)) {
//                    elem.cap = mn;
//                    to[elem.to][elem.rev].cap = 1 - mn;
//                    return true;
//                }
//            }
//            return false;
//            };
//
//        bool ok = true;
//        while (ok) {
//            ok = false;
//            for (int v : mp.begin()->second) {
//                visited.assign(n, false);
//                ok = dfs(dfs, v, v);
//                if (ok) break;
//            }
//
//            if (ok) continue;
//
//            for (int v : mp.rbegin()->second) {
//                visited.assign(n, false);
//                ok = dfs(dfs, v, v, 1);
//                if (ok) break;
//            }
//        }
//        ans.push_back({ mp.begin()->first, mp.rbegin()->first });
//    }
//
//    for (auto elem : ans) cout << elem.first << " " << elem.second << endl;
//    return 0;
//}
accuracy: 1
time_ms: 1800
memory_kb: 4112
final_rank: 12

code_snippet: #include <iostream>
#include <vector>
#include <algorithm>
#include <map>
#include <queue>
#include <set>
using namespace std;
typedef long long ll;
#define rep(i, n) for(int i = 0; i < (n); i++)

template<class T>
using vi = vector<T>;

template<class T>
using vii = vector<vi<T>>;

template<class T>
using viii = vector<vii<T>>;

struct Dinic {
private:
    const long long inf = 1e18;
    vector<int> dist; //sからの距離
    vector<int> iter; //どこまで調べたか
public:
    struct edge {
        int to, rev; long long cap;
        edge(int t = 0, int r = 0, long long c = 0LL) : to(t), rev(r), cap(c) {}
    };

    int n;
    vector<vector<edge>> to;

    Dinic(int n_ = 1) : n(n_), to(n_), dist(n_), iter(n_) {}

    void addedge(int u, int v, long long cap) {//u->vにcapの容量
        int su = (int)to[u].size(), sv = (int)to[v].size();
        to[u].push_back({ v, sv, cap });
        to[v].push_back({ u, su, 0 });
    }

    long long dfs(int v, int t, long long f) { //t: 終点, f: flow
        if (v == t) return f;
        int sv = (int)to[v].size();
        for (int& i = iter[v]; i < sv; i++) {
            edge& nv = to[v][i];
            if (dist[nv.to] <= dist[v] || nv.cap == 0) continue; //here
            long long nf = dfs(nv.to, t, min(f, nv.cap));
            if (nf > 0) {
                nv.cap -= nf;
                to[nv.to][nv.rev].cap += nf;
                return nf;
            }
        }
        return 0;
    }

    void bfs(int s) {
        dist.assign(n, -1);
        queue<int> q;
        q.push(s);
        dist[s] = 0;
        while (!q.empty()) {
            int v = q.front();
            q.pop();
            for (auto nv : to[v]) {
                if (nv.cap > 0 && dist[nv.to] < 0) {
                    dist[nv.to] = dist[v] + 1;
                    q.push(nv.to);
                }
            }
        }
    }

    long long maxflow(int s, int t) {//s:始点, t:終点
        long long res = 0, flow = 0;
        while (true) {
            bfs(s);
            if (dist[t] < 0) break;

            iter.assign(n, 0);
            while (flow = dfs(s, t, inf)) res += flow;
        };
        return res;
    }
};

int main() 
{
    vi<pair<int, int>> ans;
    int n, m;
    while (cin >> n >> m, n) {
        vi<int> u(m), v(m);
        rep(i, m) cin >> u[i] >> v[i];
        rep(i, m) u[i]--;
        rep(i, m) v[i]--;

        int high = m + 1, low = 0;
        int S = n + m, T = S + 1, T2 = S + 2;

        auto f = [&](int h, int l) {
            Dinic dn(n + m + 3);
            rep(i, m) dn.addedge(S, i, 1);
            rep(i, m) {
                dn.addedge(i, m + u[i], 1);
                dn.addedge(i, m + v[i], 1);
            }
            rep(i, n) {
                dn.addedge(m + i, T, h);
                //dn.addedge(T, m + i, l);
                dn.addedge(m + i, T2, l);
            }
            return dn;
        };

        rep(l, n + 1) {
            for(int h = l; h <= min(n, l + high - low); h++) {
                Dinic dn = f(h, l);
                
                int flow = dn.maxflow(S, T);
                if (flow < m) continue;

                int flow_l = dn.maxflow(T, T2);
                if (flow_l < l * n) continue;

                if (h - l <= high - low) {
                    high = h;
                    low = l;
                    break;
                }
            }
        }
        ans.push_back({ low, high });
    }
    for (auto elem : ans) cout << elem.first << " " << elem.second << endl;
	return 0;
}


//
//struct edge {
//    int to, cap, rev;
//};
//
//int main()
//{
//    vi<pair<int, int>> ans;
//    int n, m;
//    while (cin >> n >> m, n) {
//        vii<edge> to(n);
//        vi<int> deg(n);
//        rep(i, m) {
//            int u, v;
//            cin >> u >> v;
//            u--, v--;
//            int su = (int)to[u].size();
//            int sv = (int)to[v].size();
//            to[u].push_back({ v, 0, sv });
//            to[v].push_back({ u, 1, su });
//            deg[u]++;
//        }
//
//        map<int, set<int>> mp;
//        rep(i, n) mp[deg[i]].insert(i);
//
//        vi<bool> visited(n);
//        auto dfs = [&](auto dfs, int s, int v, int mn = 0)->bool {
//            visited[v] = true;
//            if ((!mn && deg[s] + 2 <= deg[v]) 
//                || (mn && deg[v] + 2 <= deg[s])) {
//                mp[deg[v]].erase(v);
//                mp[deg[s]].erase(s);
//                if ((int)mp[deg[v]].size() == 0) mp.erase(deg[v]);
//                if ((int)mp[deg[s]].size() == 0) mp.erase(deg[s]);
//
//                if (!mn) {
//                    deg[v]--;
//                    deg[s]++;
//                }
//                else {
//                    deg[v]++;
//                    deg[s]--;
//                }
//
//                mp[deg[v]].insert(v);
//                mp[deg[s]].insert(s);
//                return true;
//            }
//            for (auto& elem : to[v]) {
//                if (elem.cap == mn || visited[elem.to]) continue;
//                if (dfs(dfs, s, elem.to, mn)) {
//                    elem.cap = mn;
//                    to[elem.to][elem.rev].cap = 1 - mn;
//                    return true;
//                }
//            }
//            return false;
//            };
//
//        bool ok = true;
//        while (ok) {
//            ok = false;
//            for (int v : mp.begin()->second) {
//                visited.assign(n, false);
//                ok = dfs(dfs, v, v);
//                if (ok) break;
//            }
//
//            if (ok) continue;
//
//            for (int v : mp.rbegin()->second) {
//                visited.assign(n, false);
//                ok = dfs(dfs, v, v, 1);
//                if (ok) break;
//            }
//        }
//        ans.push_back({ mp.begin()->first, mp.rbegin()->first });
//    }
//
//    for (auto elem : ans) cout << elem.first << " " << elem.second << endl;
//    return 0;
//}
accuracy: 1
time_ms: 1810
memory_kb: 4112
final_rank: 13

code_snippet: #include <iostream>
#include <vector>
#include <algorithm>
#include <map>
#include <queue>
#include <set>
using namespace std;
typedef long long ll;
#define rep(i, n) for(int i = 0; i < (n); i++)

template<class T>
using vi = vector<T>;

template<class T>
using vii = vector<vi<T>>;

template<class T>
using viii = vector<vii<T>>;

struct Dinic {
private:
    const long long inf = 1e18;
    vector<int> dist; //sからの距離
    vector<int> iter; //どこまで調べたか
public:
    struct edge {
        int to, rev; long long cap;
        edge(int t = 0, int r = 0, long long c = 0LL) : to(t), rev(r), cap(c) {}
    };

    int n;
    vector<vector<edge>> to;

    Dinic(int n_ = 1) : n(n_), to(n_), dist(n_), iter(n_) {}

    void addedge(int u, int v, long long cap) {//u->vにcapの容量
        int su = (int)to[u].size(), sv = (int)to[v].size();
        to[u].push_back({ v, sv, cap });
        to[v].push_back({ u, su, 0 });
    }

    long long dfs(int v, int t, long long f) { //t: 終点, f: flow
        if (v == t) return f;
        int sv = (int)to[v].size();
        for (int& i = iter[v]; i < sv; i++) {
            edge& nv = to[v][i];
            if (dist[nv.to] <= dist[v] || nv.cap == 0) continue; //here
            long long nf = dfs(nv.to, t, min(f, nv.cap));
            if (nf > 0) {
                nv.cap -= nf;
                to[nv.to][nv.rev].cap += nf;
                return nf;
            }
        }
        return 0;
    }

    void bfs(int s) {
        dist.assign(n, -1);
        queue<int> q;
        q.push(s);
        dist[s] = 0;
        while (!q.empty()) {
            int v = q.front();
            q.pop();
            for (auto nv : to[v]) {
                if (nv.cap > 0 && dist[nv.to] < 0) {
                    dist[nv.to] = dist[v] + 1;
                    q.push(nv.to);
                }
            }
        }
    }

    long long maxflow(int s, int t) {//s:始点, t:終点
        long long res = 0, flow = 0;
        while (true) {
            bfs(s);
            if (dist[t] < 0) break;

            iter.assign(n, 0);
            while (flow = dfs(s, t, inf)) res += flow;
        };
        return res;
    }
};

int main() 
{
    vi<pair<int, int>> ans;
    int n, m;
    while (cin >> n >> m, n) {
        vi<int> u(m), v(m);
        rep(i, m) cin >> u[i] >> v[i];
        rep(i, m) u[i]--;
        rep(i, m) v[i]--;

        int high = m + 1, low = 0;
        int S = n + m, T = S + 1, T2 = S + 2;

        auto f = [&](int h, int l) {
            Dinic dn(n + m + 3);
            rep(i, m) dn.addedge(S, i, 1);
            rep(i, m) {
                dn.addedge(i, m + u[i], 1);
                dn.addedge(i, m + v[i], 1);
            }
            rep(i, n) {
                dn.addedge(m + i, T, h);
                //dn.addedge(T, m + i, l);
                dn.addedge(m + i, T2, l);
            }
            return dn;
        };

        rep(l, n + 1) {
            for(int h = (high == m + 1 ? l : max(high, l)); h <= min(n, l + high - low); h++) {
                Dinic dn = f(h, l);
                
                int flow = dn.maxflow(S, T);
                if (flow < m) continue;

                int flow_l = dn.maxflow(T, T2);
                if (flow_l < l * n) continue;

                if (h - l <= high - low) {
                    high = h;
                    low = l;
                    break;
                }
            }
        }
        ans.push_back({ low, high });
    }
    for (auto elem : ans) cout << elem.first << " " << elem.second << endl;
	return 0;
}


//
//struct edge {
//    int to, cap, rev;
//};
//
//int main()
//{
//    vi<pair<int, int>> ans;
//    int n, m;
//    while (cin >> n >> m, n) {
//        vii<edge> to(n);
//        vi<int> deg(n);
//        rep(i, m) {
//            int u, v;
//            cin >> u >> v;
//            u--, v--;
//            int su = (int)to[u].size();
//            int sv = (int)to[v].size();
//            to[u].push_back({ v, 0, sv });
//            to[v].push_back({ u, 1, su });
//            deg[u]++;
//        }
//
//        map<int, set<int>> mp;
//        rep(i, n) mp[deg[i]].insert(i);
//
//        vi<bool> visited(n);
//        auto dfs = [&](auto dfs, int s, int v, int mn = 0)->bool {
//            visited[v] = true;
//            if ((!mn && deg[s] + 2 <= deg[v]) 
//                || (mn && deg[v] + 2 <= deg[s])) {
//                mp[deg[v]].erase(v);
//                mp[deg[s]].erase(s);
//                if ((int)mp[deg[v]].size() == 0) mp.erase(deg[v]);
//                if ((int)mp[deg[s]].size() == 0) mp.erase(deg[s]);
//
//                if (!mn) {
//                    deg[v]--;
//                    deg[s]++;
//                }
//                else {
//                    deg[v]++;
//                    deg[s]--;
//                }
//
//                mp[deg[v]].insert(v);
//                mp[deg[s]].insert(s);
//                return true;
//            }
//            for (auto& elem : to[v]) {
//                if (elem.cap == mn || visited[elem.to]) continue;
//                if (dfs(dfs, s, elem.to, mn)) {
//                    elem.cap = mn;
//                    to[elem.to][elem.rev].cap = 1 - mn;
//                    return true;
//                }
//            }
//            return false;
//            };
//
//        bool ok = true;
//        while (ok) {
//            ok = false;
//            for (int v : mp.begin()->second) {
//                visited.assign(n, false);
//                ok = dfs(dfs, v, v);
//                if (ok) break;
//            }
//
//            if (ok) continue;
//
//            for (int v : mp.rbegin()->second) {
//                visited.assign(n, false);
//                ok = dfs(dfs, v, v, 1);
//                if (ok) break;
//            }
//        }
//        ans.push_back({ mp.begin()->first, mp.rbegin()->first });
//    }
//
//    for (auto elem : ans) cout << elem.first << " " << elem.second << endl;
//    return 0;
//}
accuracy: 1
time_ms: 1260
memory_kb: 4108
final_rank: 6

code_snippet: #include <iostream>
#include <vector>
#include <algorithm>
#include <map>
#include <queue>
#include <set>
using namespace std;
typedef long long ll;
#define rep(i, n) for(int i = 0; i < (n); i++)

template<class T>
using vi = vector<T>;

template<class T>
using vii = vector<vi<T>>;

template<class T>
using viii = vector<vii<T>>;

struct Dinic {
private:
    const long long inf = 1e18;
    vector<int> dist; //sからの距離
    vector<int> iter; //どこまで調べたか
public:
    struct edge {
        int to, rev; long long cap;
        edge(int t = 0, int r = 0, long long c = 0LL) : to(t), rev(r), cap(c) {}
    };

    int n;
    vector<vector<edge>> to;

    Dinic(int n_ = 1) : n(n_), to(n_), dist(n_), iter(n_) {}

    void addedge(int u, int v, long long cap) {//u->vにcapの容量
        int su = (int)to[u].size(), sv = (int)to[v].size();
        to[u].push_back({ v, sv, cap });
        to[v].push_back({ u, su, 0 });
    }

    long long dfs(int v, int t, long long f) { //t: 終点, f: flow
        if (v == t) return f;
        int sv = (int)to[v].size();
        for (int& i = iter[v]; i < sv; i++) {
            edge& nv = to[v][i];
            if (dist[nv.to] <= dist[v] || nv.cap == 0) continue; //here
            long long nf = dfs(nv.to, t, min(f, nv.cap));
            if (nf > 0) {
                nv.cap -= nf;
                to[nv.to][nv.rev].cap += nf;
                return nf;
            }
        }
        return 0;
    }

    void bfs(int s) {
        dist.assign(n, -1);
        queue<int> q;
        q.push(s);
        dist[s] = 0;
        while (!q.empty()) {
            int v = q.front();
            q.pop();
            for (auto nv : to[v]) {
                if (nv.cap > 0 && dist[nv.to] < 0) {
                    dist[nv.to] = dist[v] + 1;
                    q.push(nv.to);
                }
            }
        }
    }

    long long maxflow(int s, int t) {//s:始点, t:終点
        long long res = 0, flow = 0;
        while (true) {
            bfs(s);
            if (dist[t] < 0) break;

            iter.assign(n, 0);
            while (flow = dfs(s, t, inf)) res += flow;
        };
        return res;
    }
};

int main() 
{
    vi<pair<int, int>> ans;
    int n, m;
    while (cin >> n >> m, n) {
        vi<int> u(m), v(m);
        rep(i, m) cin >> u[i] >> v[i];
        rep(i, m) u[i]--;
        rep(i, m) v[i]--;

        int high = m, low = 0;
        int S = n + m, T = S + 1, T2 = S + 2;

        auto f = [&](int h, int l) {
            Dinic dn(n + m + 3);
            rep(i, m) dn.addedge(S, i, 1);
            rep(i, m) {
                dn.addedge(i, m + u[i], 1);
                dn.addedge(i, m + v[i], 1);
            }
            rep(i, n) {
                dn.addedge(m + i, T, h);
                //dn.addedge(T, m + i, l);
                dn.addedge(m + i, T2, l);
            }
            return dn;
        };

        rep(l, n + 1) {
            for(int h = l; h <= min(n, l + high - low); h++) {
                Dinic dn = f(h, l);
                
                int flow = dn.maxflow(S, T);
                if (flow < m) continue;

                int flow_l = dn.maxflow(T, T2);
                if (flow_l < l * n) continue;

                if (h - l <= high - low) {
                    high = h;
                    low = l;
                    break;
                }
            }
        }
        ans.push_back({ low, high });
    }
    for (auto elem : ans) cout << elem.first << " " << elem.second << endl;
	return 0;
}
accuracy: 1
time_ms: 1790
memory_kb: 4096
final_rank: 10

code_snippet: #include <iostream>
#include <vector>
#include <algorithm>
#include <map>
#include <queue>
#include <set>
using namespace std;
typedef long long ll;
#define rep(i, n) for(int i = 0; i < (n); i++)

template<class T>
using vi = vector<T>;

template<class T>
using vii = vector<vi<T>>;

template<class T>
using viii = vector<vii<T>>;

struct edge {
    int to, cap, rev;
};

int main()
{
    vi<pair<int, int>> ans;
    int n, m;
    while (cin >> n >> m, n) {
        vii<edge> to(n);
        vi<int> deg(n);
        rep(i, m) {
            int u, v;
            cin >> u >> v;
            u--, v--;
            int su = (int)to[u].size();
            int sv = (int)to[v].size();
            to[u].push_back({ v, 0, sv });
            to[v].push_back({ u, 1, su });
            deg[u]++;
        }

        map<int, set<int>> mp;
        rep(i, n) mp[deg[i]].insert(i);

        vi<bool> visited(n);
        auto dfs = [&](auto dfs, int s, int v, int mn = 0)->bool {
            visited[v] = true;
            if ((!mn && deg[s] + 2 <= deg[v]) 
                || (mn && deg[v] + 2 <= deg[s])) {
                mp[deg[v]].erase(v);
                mp[deg[s]].erase(s);
                if ((int)mp[deg[v]].size() == 0) mp.erase(deg[v]);
                if ((int)mp[deg[s]].size() == 0) mp.erase(deg[s]);

                if (!mn) {
                    deg[v]--;
                    deg[s]++;
                }
                else {
                    deg[v]++;
                    deg[s]--;
                }

                mp[deg[v]].insert(v);
                mp[deg[s]].insert(s);
                return true;
            }
            for (auto& elem : to[v]) {
                if (elem.cap == mn || visited[elem.to]) continue;
                if (dfs(dfs, s, elem.to, mn)) {
                    elem.cap = mn;
                    to[elem.to][elem.rev].cap = 1 - mn;
                    return true;
                }
            }
            return false;
            };

        bool ok = true;
        while (ok) {
            ok = false;
            for (int v : mp.begin()->second) {
                visited.assign(n, false);
                ok = dfs(dfs, v, v);
                if (ok) break;
            }

            if (ok) continue;

            for (int v : mp.rbegin()->second) {
                visited.assign(n, false);
                ok = dfs(dfs, v, v, 1);
                if (ok) break;
            }
        }
        ans.push_back({ mp.begin()->first, mp.rbegin()->first });
    }

    for (auto elem : ans) cout << elem.first << " " << elem.second << endl;
    return 0;
}
accuracy: 1
time_ms: 10
memory_kb: 3580
final_rank: 2