/**
 * code generated by JHelper
 * More info: https://github.com/AlexeyDmitriev/JHelper
 * @author Egor Kulikov
 */

#include <iostream>
#include <fstream>

#include <bits/stdc++.h>
//
// Created by kulikov on 10/17/2019.
//

#ifndef JHELPER_EXAMPLE_PROJECT_INPUT_H
#define JHELPER_EXAMPLE_PROJECT_INPUT_H

//
// Created by egor on 30.10.2019.
//

#ifndef JHELPER_EXAMPLE_PROJECT_GENERAL_H
#define JHELPER_EXAMPLE_PROJECT_GENERAL_H



using namespace std;

typedef long long longint;
typedef vector<int> vi;
typedef pair<int, int> pii;

const int MAX_INT = 2147483647;

const double PI = atan(1) * 4;

const int DX_KNIGHT[] = {2, 1, -1, -2, -2, -1, 1, 2};
const int DY_KNIGHT[] = {1, 2, 2, 1, -1, -2, -2, -1};
const int DX4[] = {1, 0, -1, 0};
const int DY4[] = {0, 1, 0, -1};

bool isValidCell(int x, int y, int rows, int cols) {
    return x >= 0 && y >= 0 && x < rows && y < cols;
}

void decreaseByOne() {
}

template <typename T, class...Vargs>
void decreaseByOne(vector<T>& arr, Vargs...arrs) {
    for (int& i : arr) {
        i--;
    }
    decreaseByOne(arrs...);
}

#endif //JHELPER_EXAMPLE_PROJECT_GENERAL_H


class Input {
public:
    enum ErrorType {
        OK,
        END_OF_FILE,
        UNEXPECTED_SYMBOL,
        INVALID_CALL
    };

private:
    istream& in;
    bool exhausted = false;
    ErrorType error = OK;
    int get();
    template<typename T> T readInteger();
    int skipWhitespace();

public:
    Input(istream& in);
    int readInt();
    longint readLong();
    string readString();
    vector<int> readIntArray(int size);
    template<typename T> T readType();
    template<typename T, typename U> pair<T, U> readType();
    template<typename T> vector<T> readArray(int size);
    template<typename T1, typename T2> tuple<vector<T1>, vector<T2> > readArrays(int size);
    template<typename T1, typename T2, typename T3> tuple<vector<T1>, vector<T2>, vector<T3> > readArrays(int size);
    template<typename T1, typename T2, typename T3, typename T4>
        tuple<vector<T1>, vector<T2>, vector<T3>, vector<T4> > readArrays(int size);
    template<typename T1, typename T2, typename T3, typename T4, typename T5>
        tuple<vector<T1>, vector<T2>, vector<T3>, vector<T4>, vector<T5> > readArrays(int size);
    template<typename T> vector<vector<T> > readTable(int rows, int cols);

    string readLine();
};

inline bool isWhitespace(int c) {
    return isspace(c) || c == EOF;
}

int Input::skipWhitespace() {
    int c;
    do {
        c = get();
        if (exhausted) {
            error = END_OF_FILE;
            return c;
        }
    } while (isWhitespace(c));
    return c;
}

Input::Input(std::istream &in) : in(in) {
}

inline int Input::get() {
    int result = in.get();
    if (result == EOF) {
        exhausted = true;
    }
    return result;
}

template<typename T>
T Input::readType() {
    error = INVALID_CALL;
    return nullptr;
}

template<typename T>
T Input::readInteger() {
    error = OK;
    int c = skipWhitespace();
    if (error != OK) {
        return 0;
    }
    int sgn = 1;
    if (c == '-') {
        sgn = -1;
        c = get();
    }
    T res = 0;
    do {
        if (!isdigit(c)) {
            error = UNEXPECTED_SYMBOL;
            return 0;
        }
        res *= 10;
        res += c - '0';
        c = get();
    } while (!isWhitespace(c));
    return res * sgn;
}

template<>
int Input::readType() {
    return readInteger<int>();
}

template<>
longint Input::readType() {
    return readInteger<longint>();
}

template<>
char Input::readType() {
    error = OK;
    int c = skipWhitespace();
    if (error != OK) {
        return 0;
    }
    return c;
}

template<>
string Input::readType() {
    error = OK;
    int c = skipWhitespace();
    if (error != OK) {
        return "";
    }
    vector<char> res;
    do {
        if (error != OK) {
            return "";
        }
        res.push_back(c);
    } while (!isWhitespace(c = get()));
    return string(res.begin(), res.end());
}

inline int Input::readInt() {
    return readType<int>();
}

inline longint Input::readLong() {
    return readType<longint>();
}

template<typename T>
vector<T> Input::readArray(int size) {
    vector<T> res;
    res.reserve(size);
    for (int i = 0; i < size; i++) {
        res.push_back(readType<T>());
        if (error != OK) {
            res.clear();
            return res;
        }
    }
    return res;
}

vector<int> Input::readIntArray(int size) {
    return readArray<int>(size);
}

template<typename T1, typename T2>
tuple<vector<T1>, vector<T2> > Input::readArrays(int size) {
    vector<T1> v1;
    vector<T2> v2;
    v1.reserve(size);
    v2.reserve(size);
    for (int i = 0; i < size; ++i) {
        v1.push_back(readType<T1>());
        v2.push_back(readType<T2>());
    }
    return make_tuple(v1, v2);
}

string Input::readString() {
    return readType<string>();
}

template<typename T>
vector<vector<T>> Input::readTable(int rows, int cols) {
    vector<vector<T> > result;
    result.reserve(rows);
    for (int i = 0; i < rows; ++i) {
        result.push_back(readArray<T>(cols));
    }
    return result;
}

string Input::readLine() {
    error = OK;
    int c = skipWhitespace();
    if (error != OK) {
        return "";
    }
    int length = 0;
    vector<char> res;
    do {
        if (error != OK) {
            return "";
        }
        res.push_back(c);
        if (!isWhitespace(c)) {
            length = res.size();
        }
        c = get();
    } while (c != '\n' && c != '\r' && c != EOF);
    return string(res.begin(), res.begin() + length);
}

template<typename T1, typename T2, typename T3>
tuple<vector<T1>, vector<T2>, vector<T3> > Input::readArrays(int size) {
    vector<T1> v1;
    vector<T2> v2;
    vector<T3> v3;
    v1.reserve(size);
    v2.reserve(size);
    v3.reserve(size);
    for (int i = 0; i < size; ++i) {
        v1.push_back(readType<T1>());
        v2.push_back(readType<T2>());
        v3.push_back(readType<T3>());
    }
    return make_tuple(v1, v2, v3);
}

template<typename T1, typename T2, typename T3, typename T4>
tuple<vector<T1>, vector<T2>, vector<T3>, vector<T4> > Input::readArrays(int size) {
    vector<T1> v1;
    vector<T2> v2;
    vector<T3> v3;
    vector<T4> v4;
    v1.reserve(size);
    v2.reserve(size);
    v3.reserve(size);
    v4.reserve(size);
    for (int i = 0; i < size; ++i) {
        v1.push_back(readType<T1>());
        v2.push_back(readType<T2>());
        v3.push_back(readType<T3>());
        v4.push_back(readType<T4>());
    }
    return make_tuple(v1, v2, v3, v4);
}

template<typename T1, typename T2, typename T3, typename T4, typename T5>
tuple<vector<T1>, vector<T2>, vector<T3>, vector<T4>, vector<T5> > Input::readArrays(int size) {
    vector<T1> v1;
    vector<T2> v2;
    vector<T3> v3;
    vector<T4> v4;
    vector<T5> v5;
    v1.reserve(size);
    v2.reserve(size);
    v3.reserve(size);
    v4.reserve(size);
    v5.reserve(size);
    for (int i = 0; i < size; ++i) {
        v1.push_back(readType<T1>());
        v2.push_back(readType<T2>());
        v3.push_back(readType<T3>());
        v4.push_back(readType<T4>());
        v5.push_back(readType<T5>());
    }
    return make_tuple(v1, v2, v3, v4, v5);
}

#endif //JHELPER_EXAMPLE_PROJECT_INPUT_H

//
// Created by egor on 31.10.2019.
//

#ifndef JHELPER_EXAMPLE_PROJECT_OUTPUT_H
#define JHELPER_EXAMPLE_PROJECT_OUTPUT_H



class Output {
private:
    ostream& out;
    template<typename T> void printSingle(const T& value);
    template<typename T> void printSingle(const vector<T>& value);
    template<typename T, typename U> void printSingle(const pair<T, U>& value);

public:
    Output(ostream& out);
    void print();
    template<typename T, typename...Targs>void print(const T& first, const Targs... args);
    template<typename...Targs>void printLine(const Targs... args);
    void flush();
};

Output::Output(ostream &out) : out(out) {
    out << setprecision(12);
}

void Output::print() {
}

template<typename T, typename... Targs>
void Output::print(const T& first, const Targs... args) {
    printSingle(first);
    if (sizeof...(args) != 0) {
        out << ' ';
        print(args...);
    }
}

template<typename T>
void Output::printSingle(const T& value) {
    out << value;
}

template<typename... Targs>
void Output::printLine(const Targs... args) {
    print(args...);
    out << '\n';
}

void Output::flush() {
    out.flush();
}

template<typename T>
void Output::printSingle(const vector<T>& array) {
    unsigned int size = array.size();
    for (int i = 0; i < size; ++i) {
        out << array[i];
        if (i + 1 != size) {
            out << ' ';
        }
    }
}

template<typename T, typename U>
void Output::printSingle(const pair<T, U>& value) {
    out << value.first << ' ' << value.second;
}

#endif //JHELPER_EXAMPLE_PROJECT_OUTPUT_H

//
// Created by egor on 31.10.2019.
//

#ifndef JHELPER_EXAMPLE_PROJECT_ALGO_H
#define JHELPER_EXAMPLE_PROJECT_ALGO_H



template <class V>
inline void sort(V& v) {
    sort(v.begin(), v.end());
}

template <typename T, class C>
inline void sort(vector<T>& v, C c) {
    sort(v.begin(), v.end(), c);
}

template <typename T>
inline void unique(vector<T>& v) {
    v.resize(unique(v.begin(), v.end()) - v.begin());
}

template <typename T>
inline T accumulate(const vector<T>& v) {
    return accumulate(v.begin(), v.end(), T(0));
}

vi createOrder(int n) {
    vi order(n);
    for (int i = 0; i < n; i++) {
        order[i] = i;
    }
    return order;
}

template <typename T>
inline void reverse(vector<T>& v) {
    reverse(v.begin(), v.end());
}

template <typename T>
inline T minElement(const vector<T>& v) {
    return *min_element(v.begin(), v.end());
}

template <typename T>
inline T maxElement(const vector<T>& v) {
    return *max_element(v.begin(), v.end());
}

template <typename T>
inline vector<vector<T> > makeArray(int a, int b) {
    return vector<vector<T> >(a, vector<T>(b));
}

template <typename T>
inline vector<vector<vector<T> > > makeArray(int a, int b, int c) {
    return vector<vector<vector<T> > >(a, makeArray<T>(b, c));
}

template <typename T>
inline vector<vector<T> > makeArray(int a, int b, T init) {
    return vector<vector<T> >(a, vector<T>(b, init));
}

template <typename T>
inline vector<vector<vector<T> > > makeArray(int a, int b, int c, T init) {
    return vector<vector<vector<T> > >(a, makeArray<T>(b, c, init));
}

template <typename T>
inline void addAll(vector<T>& v, const vector<T>& toAdd) {
    v.insert(v.end(), toAdd.begin(), toAdd.end());
}

template <typename T>
inline void fill(vector<T>& v, const T& val) {
    fill(v.begin(), v.end(), val);
}

#endif //JHELPER_EXAMPLE_PROJECT_ALGO_H

//
// Created by egor on 01.11.2019.
//

#ifndef JHELPER_EXAMPLE_PROJECT_NUMBERS_H
#define JHELPER_EXAMPLE_PROJECT_NUMBERS_H


//
// Created by egor on 31.10.2019.
//

#ifndef JHELPER_EXAMPLE_PROJECT_MODULO_H
#define JHELPER_EXAMPLE_PROJECT_MODULO_H



const int MOD7 = 1000000007;
const int MOD9 = 1000000009;
const int MODF = 998244353;

int mod = MOD7;

class ModuloInt {
private:
    longint n;

public:
    ModuloInt() : n(0) {}
    ModuloInt(int n) {
        n %= mod;
        if (n < 0) {
            n += mod;
        }
        this->n = n;
    }
    ModuloInt(const ModuloInt& n) = default;
    ModuloInt& operator +=(const ModuloInt& other);
    ModuloInt& operator -=(const ModuloInt& other);
    ModuloInt& operator *=(const ModuloInt& other);
    ModuloInt operator -();
    friend ostream&operator <<(ostream& out, const ModuloInt& val);
};

ModuloInt &ModuloInt::operator+=(const ModuloInt& other) {
    n += other.n;
    if (n >= mod) {
        n -= mod;
    }
    return *this;
}

ModuloInt &ModuloInt::operator-=(const ModuloInt& other) {
    n -= other.n;
    if (n < 0) {
        n += mod;
    }
    return *this;
}

ModuloInt &ModuloInt::operator*=(const ModuloInt& other) {
    n *= other.n;
    n %= mod;
    return *this;
}

ModuloInt operator +(const ModuloInt& a, const ModuloInt& b) {
    return ModuloInt(a) += b;
}

ModuloInt operator -(const ModuloInt& a, const ModuloInt& b) {
    return ModuloInt(a) -= b;
}

ModuloInt operator *(const ModuloInt& a, const ModuloInt& b) {
    return ModuloInt(a) *= b;
}

ostream& operator <<(ostream& out, const ModuloInt& val) {
    return out << val.n;
}

ModuloInt ModuloInt::operator-() {
    if (n == 0) {
        return 0;
    }
    return ModuloInt(mod - n);
}

#endif //JHELPER_EXAMPLE_PROJECT_MODULO_H


template <typename T>
T gcd(T a, T b) {
    a = abs(a);
    b = abs(b);
    while (b != 0) {
        a = a % b;
        swap(a, b);
    }
    return a;
}

template <typename T>
T power(const T& a, longint b) {
    if (b == 0) {
        return 1;
    }
    if ((b & 1) == 0) {
        T res = power(a, b >> 1);
        return res * res;
    } else {
        return power(a, b - 1) * a;
    }
}

template <typename T>
vector<T> generateFactorial(int length) {
    vector<T> result(length);
    if (length > 0) {
        result[0] = 1;
    }
    for (int i = 1; i < length; i++) {
        result[i] = result[i - 1] * i;
    }
    return result;
}

template <typename T>
vector<T> generateReverse(int length) {
    vector<T> result(length);
    if (length > 1) {
        result[1] = 1;
    }
    for (int i = 2; i < length; i++) {
        result[i] = -(mod / i) * result[mod % i];
    }
    return result;
}

template <typename T>
vector<T> generatePowers(T base, int length) {
    vector<T> result(length);
    if (length > 0) {
        result[0] = 1;
    }
    for (int i = 1; i < length; i++) {
        result[i] = result[i - 1] * base;
    }
    return result;
}

template <typename T>
vector<T> generateReverseFactorial(int length) {
    auto result = generateReverse<T>(length);
    if (length > 0) {
        result[0] = 1;
    }
    for (int i = 1; i < length; i++) {
        result[i] *= result[i - 1];
    }
    return result;
}

template <typename T>
class Combinations {
private:
    vector<T> fact;
    vector<T> revFactorial;

public:
    Combinations(int length) {
        fact = generateFactorial<T>(length);
        revFactorial = generateReverseFactorial<T>(length);
    }

public:
    T c(int n, int k) {
        if (k < 0 || k > n) {
            return 0;
        }
        return fact[n] * revFactorial[k] * revFactorial[n - k];
    }

    T factorial(int n) {
        return fact[n];
    }

    T reverseFactorial(int n) {
        return revFactorial[n];
    }
};

#endif //JHELPER_EXAMPLE_PROJECT_NUMBERS_H


using namespace std;

class DBalanceBeam {
public:
	void solve(std::istream& inp, std::ostream& outp) {
        Input in(inp);
        Output out(outp);

        int n = in.readInt();
        vector<pair<longint, longint> > beams(n);
        for (int i = 0; i < n; ++i) {
            longint a = in.readInt();
            longint b = in.readInt();
            beams[i] = make_pair(a, b);
        }
        longint delta = 0;
        vector<pair<longint, longint> > bad;
        multiset<longint, greater<longint> > take;
        longint sum = 0;
        for (const auto& p : beams) {
            if (p.first <= p.second) {
                delta += p.second - p.first;
                take.insert(p.second);
                sum += p.second;
            } else {
                bad.push_back(p);
            }
        }
        if (take.empty() || delta == 0) {
            out.printLine(0, 1);
            return;
        }
        while (sum >= delta + *take.begin()) {
            sum -= *take.begin();
            take.erase(take.begin());
        }
        longint last = *take.begin();
        longint remaining = last - (sum - delta);
        sort(bad, [](const pair<longint, longint>& x, const pair<longint, longint>& y) -> bool {if (x.first != y.first) {return x.first < y.first;} return x.second > y.second;});
        int alpha = 0;
        for (const auto& p : bad) {
            longint a = p.first;
            if (last <= a) {
                break;
            }
            if (remaining >= a) {
                remaining -= a;
                take.insert(p.second);
                alpha++;
                continue;
            }
            longint wasLast = last;
            longint wasRemaining = remaining;
            take.erase(take.begin());
            take.insert(p.second);
            longint newLast = *take.begin();
            longint need = a - remaining;
            if (newLast >= a) {
                last = newLast;
                remaining = last - need;
                alpha++;
                continue;
            }
/*            if (longint(need) * wasLast + longint(newLast) * wasRemaining < longint(newLast) * wasLast) {
                last = newLast;
                remaining = last - need;
            } else {*/
                take.erase(take.find(p.second));
                take.insert(wasLast);
                remaining = wasRemaining;
                last = wasLast;
                break;
//            }
//            alpha++;
        }
        longint bestRemaining = remaining;
        longint bestLast = last;
        for (int i = alpha; i < bad.size(); i++) {
            longint a = bad[i].first;
            const pair<long, long>& p = bad[i];
            longint wasLast = last;
            longint wasRemaining = remaining;
            take.erase(take.begin());
            take.insert(p.second);
            longint newLast = *take.begin();
            longint need = a - remaining;
            longint newRemaining = newLast - need;
            if (newRemaining * bestLast > bestRemaining * newLast) {
                bestRemaining = newRemaining;
                bestLast = newLast;
            }
            take.erase(take.find(p.second));
            take.insert(wasLast);
            remaining = wasRemaining;
            last = wasLast;
        }
        last = bestLast;
        remaining = bestRemaining;
        longint p = longint(take.size() - 1) * last + remaining;
        longint q = longint(last) * n;
        longint g = gcd(p, q);
        p /= g;
        q /= g;
        out.printLine(p, q);
	}
};


int main() {
    std::ios::sync_with_stdio(false);
	DBalanceBeam solver;
	std::istream& in(std::cin);
	std::ostream& out(std::cout);
	solver.solve(in, out);
	return 0;
}


Measured memory_bytes: 7920