#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
#define x first
#define y second
typedef long long ll;
template<typename T,typename F>T bsl(T l,T h,const F&f){T r=-1,m;while(l<=h){m=(l+h)/2;if(f(m)){h=m-1;r=m;}else{l=m+1;}}return r;}
/** Successive shortest paths algorithm. Runs in O(maxFlow * (|E| + sumCosts)). */
template<typename Cap = int, typename Cost = int>
struct SSPA {
struct Edge{
Cost c; Cap f; int to, rev;
Edge(int _to, Cost _c, Cap _f, int _rev):c(_c), f(_f), to(_to), rev(_rev){}
};
int N, source, sink;
vector<vector<Edge>> G;
SSPA(int N, int source, int sink): N(N), source(source), sink(sink), G(N) {}
void addEdge(int a, int b, Cap cap, Cost cost) {
assert(cap>=0);
assert(a>=0&&a<N&&b>=0&&b<N);
if(a==b){assert(cost>=0); return;}
G[a].emplace_back(b, cost, cap, G[b].size());
G[b].emplace_back(a, -cost, 0, G[a].size()-1);
}
pair<Cap, Cost> minCostMaxFlow() {
/* Vertex potentials. These are maintained so that all edges with non-zero
* residual have non-negative length. Thus, Dijkstra can be used instead of
* Bellman-Ford in each step of the algorithm. */
vector<Cost> Pi(N, 0);
Cost infty = std::numeric_limits<Cost>::max();
Cap totFlow = 0;
Cost totCost = 0;
while (true) {
vector<Cost> D(N, infty);
vector<int> Prev(N, -1);
D[source] = 0;
vector<vector<int>> Q{{source}};
for (int i = 0; i < Q.size(); ++i) {
for (int j = 0; j < Q[i].size(); ++j) {
int u = Q[i][j];
if (D[u] < i) continue;
for (auto &e: G[u]) {
if (e.f > 0) {
Cost c = D[u] + e.c;
if (D[e.to] > c) {
D[e.to] = c;
while (c >= Q.size()) Q.emplace_back();
Q[c].push_back(e.to);
Prev[e.to] = u;
}
}
}
}
}
// if sink is unreachable, flow is optimal
if (D[sink] == infty) break;
// reconstruct some shortest path
int v = sink;
vector<int> Path;
while (v != -1) { Path.push_back(v); v = Prev[v]; }
reverse(Path.begin(),Path.end());
// found the minimum of the edge residuals along the path
Cap augment = std::numeric_limits<Cap>::max();
int L = Path.size();
for (int i = 0; i < L-1; ++i) {
int u = Path[i], v = Path[i+1];
for (auto&e: G[u]) {
// be careful, there might be multiedges
if (e.to == v && e.f > 0 && D[v] == D[u] + e.c) {
augment = min(augment, e.f);
break;
}
}
}
for (int i = 0; i < L-1; ++i) {
int u = Path[i], v = Path[i+1];
for (auto&e: G[u]) {
if (e.to == v && e.f > 0 && D[v] == D[u] + e.c) {
e.f -= augment;
G[v][e.rev].f += augment;
break;
}
}
}
// store the cost & flow size
Cost cost = Pi[source] - Pi[sink] + D[sink];
totFlow += augment;
totCost += cost * augment;
// remove potentials from costs
for (int i = 0; i < N; ++i) {
for (auto &e: G[i]) {
e.c -= Pi[e.to] - Pi[i];
}
}
// add potentials to costs again
for (int i = 0; i < N; ++i) {
for (auto &e: G[i]) {
e.c += (Pi[e.to] - D[e.to]) - (Pi[i] - D[i]);
}
}
// update potentials based on the results
for (int i = 0; i < N; ++i) Pi[i] -= D[i];
}
return {totFlow,totCost};
}
};
class Stock {
public:
void solve(istream& cin, ostream& cout) {
int N; cin >> N;
vector<int> A(2*N), B(2*N);
for (int i = 0; i < 2*N; ++i) {
cin >> A[i] >> B[i];
}
// find T: O(N log MAX log N)
int T = bsl(0, 1000000000, [&](int t) {
// ( (price at 0) x (-price at t) x (is initial stock) )
vector<pair<pair<int, ll>, bool>> Stocks(2*N);
for (int i = 0; i < 2*N; ++i) {
Stocks[i] = {{B[i], -(A[i]*ll(t) + B[i])}, i<N};
}
sort(Stocks.begin(),Stocks.end());
ll best = 0;
vector<ll> CanHave, Required;
for (int i = 0; i < 2*N; ++i) {
best = max(best, -Stocks[i].x.y);
if (Stocks[i].y) {
CanHave.push_back(best);
} else {
Required.push_back(-Stocks[i].x.y);
}
}
sort(CanHave.begin(),CanHave.end());
sort(Required.begin(),Required.end());
for (int i = 0; i < N; ++i) {
if (CanHave[i] < Required[i]) return false;
}
return true;
});
if (T == -1) {
cout << -1 << endl;
return;
}
vector<pair<pair<int,ll>,int>> Begin(2*N);
vector<pair<ll, int>> End(2*N);
for (int i = 0; i < 2*N; ++i) {
ll end = A[i]*ll(T) + B[i];
// bigger start => process later
// bigger end => process earlier
Begin[i] = {{B[i], -end}, i};
// bigger end => process later
// bigger id => process earlier
End[i] = {end, -i};
}
sort(Begin.begin(),Begin.end());
sort(End.begin(),End.end());
reverse(Begin.begin(),Begin.end());
reverse(End.begin(),End.end());
// SSPA, using O(dijkstra * flow)
SSPA<int,int> G(6*N+2, 6*N, 6*N+1);
for (int i = 0; i < 2*N; ++i) {
if (i != 2*N-1) {
int j = Begin[i].y;
int k = Begin[i+1].y;
G.addEdge(N+j, N+k, N, 0); // perform exchange at t=0
j = -End[i].y;
k = -End[i+1].y;
G.addEdge(3*N+j, 3*N+k, N, 0); // perform exchange at t=T
}
if (Begin[i].y < N) {
int j = Begin[i].y;
G.addEdge(6*N, j, 1, 0); // super source
G.addEdge(j, N+j, 1, 1); // exchange for something at t=0
G.addEdge(j, 3*N+j, 1, 0); // hold until t=T
}
if ((-End[i].y) >= N) {
int j = -End[i].y;
G.addEdge(N+j, 5*N+(j-N), 1, 0); // was kept from t=0
G.addEdge(3*N+j, 5*N+(j-N), 1, 1); // was exchanged at t=T
G.addEdge(5*N+(j-N), 6*N+1, 1, 0); // super sink
}
G.addEdge(N+i, 3*N+i, N, 0); // hold between 0 and T
}
auto res = G.minCostMaxFlow();
assert(res.x == N);
cout << T << ' ' << res.y << '\n';
}
};
int main() {
ios_base::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);
Stock solver;
std::istream& in(std::cin);
std::ostream& out(std::cout);
solver.solve(in, out);
return 0;
}