#include <algorithm>
#include <cassert>
#include <iostream>
#include <vector>
using namespace std;

using fraction = pair<long long, long long>;

bool bigger(fraction a, fraction b) {
    return (__int128) a.first * b.second > (__int128) b.first * a.second;
}

template<typename T>
struct fenwick_tree {
    int tree_n = 0;
    T tree_sum = 0;
    vector<T> tree;

    fenwick_tree(int n = -1) {
        if (n >= 0)
            init(n);
    }

    void init(int n) {
        tree_n = n;
        tree_sum = 0;
        tree.assign(tree_n + 1, 0);
    }

    // O(n) initialization of the Fenwick tree.
    template<typename T_array>
    void build(const T_array &initial) {
        assert((int) initial.size() == tree_n);
        tree_sum = 0;

        for (int i = 1; i <= tree_n; i++) {
            tree[i] = initial[i - 1];
            tree_sum += initial[i - 1];

            for (int k = (i & -i) >> 1; k > 0; k >>= 1)
                tree[i] += tree[i - k];
        }
    }

    // index is in [0, tree_n).
    void update(int index, const T &change) {
        assert(0 <= index && index < tree_n);
        tree_sum += change;

        for (int i = index + 1; i <= tree_n; i += i & -i)
            tree[i] += change;
    }

    // Returns the sum of the range [0, count).
    T query(int count) const {
        assert(count <= tree_n);
        T sum = 0;

        for (int i = count; i > 0; i -= i & -i)
            sum += tree[i];

        return sum;
    }

    // Returns the sum of the range [start, tree_n).
    T query_suffix(int start) const {
        return tree_sum - query(start);
    }

    // Returns the sum of the range [a, b).
    T query(int a, int b) const {
        return query(b) - query(a);
    }

    // Returns the element at index a in O(1) amortized across every index. Equivalent to query(a, a + 1).
    T get(int a) const {
        assert(0 <= a && a < tree_n);
        int above = a + 1;
        T sum = tree[above];
        above -= above & -above;

        while (a != above) {
            sum -= tree[a];
            a -= a & -a;
        }

        return sum;
    }

    // Returns the last i in [0, N] such that query(i) <= sum, or -1 if no such i exists.
    int find_last_prefix(T sum) const {
        if (sum < 0)
            return -1;

        int prefix = 0;

        for (int k = 31 - __builtin_clz(tree_n); k >= 0; k--)
            if (prefix + (1 << k) <= tree_n && tree[prefix + (1 << k)] <= sum) {
                prefix += 1 << k;
                sum -= tree[prefix];
            }

        return prefix;
    }
};


struct beam {
    int a, b, index;
};

bool compare_a(beam x, beam y) {
    return x.a < y.a;
}

bool compare_b(beam x, beam y) {
    return x.b < y.b;
}

int N;
vector<beam> beams;
fenwick_tree<long long> tree;
fenwick_tree<int> one_tree;

fraction evaluate(long long a_sum) {
    long long b_sum = tree.query(N);

    if (a_sum >= b_sum)
        return {0, 1};

    int prefix = tree.find_last_prefix(b_sum - a_sum);
    assert(prefix < N);
    long long b = tree.get(prefix);
    assert(b > 0);
    long long numer = (long long) one_tree.query(prefix + 1) * b - (a_sum - tree.query_suffix(prefix + 1));
    long long denom = b;
    return {numer, denom};
}

// fraction evaluate(long long a_sum, vector<int> B) {
//     sort(B.begin(), B.end());

//     while (!B.empty() && a_sum >= B.back()) {
//         a_sum -= B.back();
//         B.pop_back();
//     }

//     if (B.empty())
//         return {0, 1};

//     long long numer = (long long) B.size() * B.back() - a_sum;
//     long long denom = B.back();
//     return {numer, denom};
// }

double to_double(fraction f) {
    return (double) f.first / f.second;
}

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

    cin >> N;
    beams.resize(N);

    for (beam &bm : beams)
        cin >> bm.a >> bm.b;

    sort(beams.begin(), beams.end(), compare_b);

    for (int i = 0; i < N; i++)
        beams[i].index = i;

    vector<beam> new_beams;
    long long a_sum = 0;
    tree.init(N);
    one_tree.init(N);

    for (int i = 0; i < N; i++)
        if (beams[i].a <= beams[i].b) {
            a_sum += beams[i].a;
            tree.update(i, beams[i].b);
            one_tree.update(i, +1);
        } else {
            new_beams.push_back(beams[i]);
        }

    swap(beams, new_beams);
    new_beams = {};
    sort(beams.begin(), beams.end(), compare_a);
    // cerr << "initial: " << to_double(evaluate(a_sum, B)) << endl;
    int prev_index = -1, prev_a = -1, prev_b = -1;
    fraction current = evaluate(a_sum);

    for (beam &bm : beams) {
        // vector<int> new_B = B;
        // new_B.push_back(bm.b);
        // cerr << bm.a << ' ' << bm.b << ": " << to_double(evaluate(a_sum + bm.a, new_B)) << endl;
        tree.update(bm.index, bm.b);
        one_tree.update(bm.index, +1);

        if (bigger(evaluate(a_sum + bm.a), current)) {
            a_sum += bm.a;
            current = evaluate(a_sum);
            prev_index = bm.index;
            prev_a = bm.a;
            prev_b = bm.b;
            // cerr << "improved: " << to_double(evaluate(a_sum, B)) << endl;
            // cerr << "with " << bm.a << ' ' << bm.b << endl;
        } else if (prev_index >= 0) {
            // new_B.erase(find(new_B.begin(), new_B.end(), prev_b));
            tree.update(prev_index, -prev_b);
            one_tree.update(prev_index, -1);

            if (bigger(evaluate(a_sum + bm.a - prev_a), current)) {
                a_sum += bm.a - prev_a;
                current = evaluate(a_sum);
                prev_index = bm.index;
                prev_a = bm.a;
                prev_b = bm.b;
            } else {
                tree.update(prev_index, prev_b);
                one_tree.update(prev_index, +1);
                tree.update(bm.index, -bm.b);
                one_tree.update(bm.index, -1);
            }
        } else {
            tree.update(bm.index, -bm.b);
            one_tree.update(bm.index, -1);
        }
    }

    current.second *= N;
    long long G = __gcd(current.first, current.second);
    current.first /= G;
    current.second /= G;
    cout << current.first << ' ' << current.second << '\n';
}


Measured memory_bytes: 4344