#include <bits/stdc++.h>
using namespace std;
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...);
}
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();
  long long 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 <>
long long Input::readType() {
  return readInteger<long long>();
}
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 long long Input::readLong() { return readType<long long>(); }
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);
}
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;
}
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));
}
vector<int> createOrder(int n) {
  vector<int> 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);
}
const int MOD7 = 1000000007;
const int MOD9 = 1000000009;
const int MODF = 998244353;
int mod = MOD7;
class ModuloInt {
 private:
  long long 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);
}
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, long long 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]; }
};
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<int, int>> beams(n);
    for (int i = 0; i < n; ++i) {
      int a = in.readInt();
      int b = in.readInt();
      beams[i] = make_pair(a, b);
    }
    long long delta = 0;
    vector<pair<int, int>> bad;
    multiset<int> take;
    long long 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()) {
      out.printLine(0, 1);
      return;
    }
    while (sum >= delta + *take.rbegin()) {
      sum -= *take.rbegin();
      take.erase(*take.rbegin());
    }
    if (take.empty()) {
      out.printLine(0, 1);
      return;
    }
    int last = *take.rbegin();
    int remaining = last - (sum - delta);
    sort(bad);
    for (const auto& p : bad) {
      int a = p.first;
      if (last <= a) {
        break;
      }
      if (remaining >= a) {
        remaining -= a;
        take.insert(p.second);
        continue;
      }
      int wasLast = last;
      int wasRemaining = remaining;
      take.erase(*take.rbegin());
      take.insert(p.second);
      int newLast = *take.rbegin();
      int need = a - remaining;
      if (long long(need) * wasLast + long long(newLast * wasRemaining) <
          long long(newLast) * wasLast) {
        last = newLast;
        remaining = last - need;
      } else {
        take.erase(p.second);
        take.insert(wasLast);
        remaining = wasRemaining;
        last = wasLast;
        break;
      }
    }
    long long p = long long(take.size() - 1) * last + remaining;
    long long q = long long(last) * n;
    long long 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;
}
