Problem:
Mixing Solutions
Let’s prepare for an experiment with the chemical
Yokohama Yellow
, or
YY
in short. You have several containers of aqueous solution of YY. While YY is evenly dissolved in each solution, the concentration  may differ among containers. You will take arbitrary amounts of solution from some of the containers and mix them to prepare a new solution with the predetermined total amount.
Ideally, the mixed solution should contain the
target amount
of YY, but there is a problem. While the exact amount of solution in each container is known, the amount of YY in each solution is guaranteed only to fall within a certain range. Due to this uncertainty, it is difficult to match the amount of YY in the mixed solution exactly to the target amount. Still, you can ensure that the error, the difference from the target amount, will never exceed a certain limit.
To be more precise, let the target and actual amounts of YY in the mixed solution be $y_t$ mg (milligrams) and $y_a$ mg, respectively. Given the amounts of solution taken from the containers, $y_a$ is guaranteed to fall within a certain range. The
maximum error
is defined as the maximum of $|y_a - y_t|$ when $y_a$ varies  within this range.
Find the minimum achievable value of the maximum error, given that you can take any portion of the solution in each container as long as their total is equal to the predetermined amount.
Input
The input consists of a single test case of the following format.
$n$ $s$ $c$
$a_1$ $l_1$ $r_1$
$\vdots$
$a_n$ $l_n$ $r_n$
The first line contains three integers, $n$, $s$, and $c$, satisfying $1 \leq n \leq 1000$, $1 \leq s \leq 10^5$, and $0 \leq c \leq M$, where $M = 10^4$ here and in what follows. Here, $n$ denotes the number of containers of YY solution. The predetermined total amount of the mixed solution is $s$ mg, and the target amount of YY is $\frac{c}{M}s$ mg.  The $i$-th of the following $n$ lines contains three integers, $a_i$, $l_i$, and $r_i$, satisfying $1 \leq a_i \leq 10^5$ and $0 \leq l_i \leq r_i \leq M$. These integers indicate that the $i$-th container has $a_i$ mg of solution and that the amount of YY in it is guaranteed to be between $\frac{l_i}{M}a_i$ mg and $\frac{r_i}{M}a_i$ mg, inclusive. They satisfy $\sum^n_{i=1} a_i \geq s$.
Output
The minimum achievable value of the maximum error can be proven to be a rational number. Express the value as an irreducible fraction $p/q$ with $q > 0$, and output $p$ and $q$ separated by a space on a single line.
Sample Input 1
3 10 5000
10 2000 3000
10 4000 6000
10 7000 8000
Sample Output 1
1 2
Sample Input 2
2 10 5000
7 4500 5500
12 3500 6000
Sample Output 2
4 5
Sample Input 3
3 1 4159
1 1 1
1 100 100
1 10000 10000
Sample Output 3
0 1
Sample Input 4
6 12345 6789
2718 2818 2845
9045 2353 6028
7471 3526 6249
7757 2470 9369
9959 5749 6696
7627 7240 7663
Sample Output 4
23901191037 67820000

Submitted solutions:

code_snippet: //
// 有理数
//
// verified
//   AOJ 1426 Remodeling the Dungeon 2 (ICPC Asia 2024 H)
//     https://onlinejudge.u-aizu.ac.jp/problems/1464
//


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


// 有理数
template<class T> struct frac {
    // inner values
    T first, second;

    // constructor
    frac& normalize() {
        if (second < 0) first = -first, second = -second;
        T abs_first = (first >= 0 ? first : -first);
        T d = gcd(abs_first, second);
        if (d == 0) first = 0, second = 1;
        else first /= d, second /= d;
        return *this;
    }
    constexpr frac(T f = 0, T s = 1) : first(f), second(s) { 
        normalize(); 
    }
    constexpr frac& operator = (T a) { 
        *this = frac(a, 1); 
        return *this;
    }

    // comparison operators
    constexpr bool operator == (const frac &r) const {
        return this->first == r.first && this->second == r.second;
    }
    constexpr bool operator != (const frac &r) const {
        return this->first != r.first || this->second != r.second;
    }
    constexpr bool operator < (const frac &r) const {
        return this->first * r.second < this->second * r.first;
    }
    constexpr bool operator > (const frac &r) const {
        return this->first * r.second > this->second * r.first;
    }
    constexpr bool operator <= (const frac &r) const {
        return this->first * r.second <= this->second * r.first;
    }
    constexpr bool operator >= (const frac &r) const {
        return this->first * r.second >= this->second * r.first;
    }
    
    // arithmetic operators
    constexpr frac& operator += (const frac &r) {
        this->first = this->first * r.second + this->second * r.first;
        this->second *= r.second;
        this->normalize();
        return *this;
    }
    constexpr frac& operator -= (const frac &r) {
        this->first = this->first * r.second - this->second * r.first;
        this->second *= r.second;
        this->normalize();
        return *this;
    }
    constexpr frac& operator *= (const frac &r) {
        this->first *= r.first;
        this->second *= r.second;
        this->normalize();
        return *this;
    }
    constexpr frac& operator /= (const frac &r) {
        this->first *= r.second;
        this->second *= r.first;
        this->normalize();
        return *this;
    }
    constexpr frac operator + () const { return frac(*this); }
    constexpr frac operator - () const { return frac(0) - frac(*this); }
    constexpr frac operator + (const frac &r) const { return frac(*this) += r; }
    constexpr frac operator - (const frac &r) const { return frac(*this) -= r; }
    constexpr frac operator * (const frac &r) const { return frac(*this) *= r; }
    constexpr frac operator / (const frac &r) const { return frac(*this) /= r; }
    friend constexpr ostream& operator << (ostream &os, const frac<T> &x) {
        os << x.first; 
        if (x.second != 1) os << "/" << x.second;
        return os;
    }
};


//------------------------------//
// Examples
//------------------------------//

// AOJ 1426 Remodeling the Dungeon 2 (ICPC Asia 2024 H)
void ICPC_Asia_2024_J() {
    using lfrac = frac<long long>;
    const long long M = 10000;
    long long N, S, C, sumA = 0;
    cin >> N >> S >> C;
    vector<long long> A(N), L(N), R(N);
    for (int i = 0; i < N; i++) cin >> A[i] >> L[i] >> R[i], sumA += A[i];

    vector<lfrac> vw({lfrac(-1), lfrac(1)});
    for (int i = 0; i < N; i++) {
        for (int j = i+1; j < N; j++) {
            lfrac f(-L[i]+R[i]+L[j]-R[j], L[i]+R[i]-L[j]-R[j]);
            if (f >= -1 && f <= 1) vw.push_back(f);
        }
    }
    sort(vw.begin(), vw.end());
    vw.erase(unique(vw.begin(), vw.end()), vw.end());

    auto func = [&](lfrac w) -> lfrac { 
        vector<pair<lfrac,int>> v;
        for (int i = 0; i < N; i++) {
            v.emplace_back(w*lfrac(L[i]+R[i])/2 + lfrac(L[i]-R[i])/2, i);
        }
        sort(v.begin(), v.end());

        vector<lfrac> y(N, -1);
        lfrac z, limit = sumA - S;
        for (auto [val, i] : v) {
            if (limit >= 0) {
                limit -= A[i];
                y[i] = 0;
                if (limit < 0) z = -val;
            } else {
                y[i] = z + val;
            }
        }
        lfrac obj = z * S + w * C * S;
        for (int i = 0; i < N; i++) obj -= y[i] * A[i];
        return obj;
    };

    int left = 0, right = vw.size()-1;
    while (right - left > 2) {
        int m1 = (left * 2 + right) / 3, m2 = (left + right * 2) / 3;
        if (func(vw[m1]) > func(vw[m2])) right = m2;
        else left = m1;
    }
    lfrac res = max({func(vw[left]), func(vw[(left+right)/2]), func(vw[right])});
    res /= M;
    cout << res.first << " " << res.second << endl;
}


int main() {
    ICPC_Asia_2024_J();
}
accuracy: 1
time_ms: 100
memory_kb: 13396
final_rank: 6

code_snippet: // AOJ #1464 Mixing Solutions
// 2025.3.28

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

#define gc() getchar_unlocked()
#define pc(c) putchar_unlocked(c)

int Cin() {
	int n = 0; int c = gc();
	do n = 10*n + (c & 0xf), c = gc(); while (c >= '0');
	return n;
}

void Cout(ll n) {
	char b[30];
	if (!n) pc('0');
	else {
		if (n < 0) pc('-'), n = -n;
		int i = 0; while (n) b[i++] = n % 10 + '0', n /= 10;
		while (i--) pc(b[i]);
	}
}

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

template <class T>
struct Fraction {
    T n, d;
    Fraction(T num = 0, T den = 1) : n(num), d(den) { norm(); }
    Fraction& norm() {
        if (d < 0) { n = -n; d = -d; }
        T g = gcd(n < 0 ? -n : n, d < 0 ? -d : d);
        if (g == 0) { n = 0; d = 1; }
        else { n /= g; d /= g; }
        return *this;
    }
    Fraction& operator=(T a) { *this = Fraction(a, 1); return *this; }
    bool operator==(const Fraction &r) const { return n * r.d == d * r.n; }
    bool operator!=(const Fraction &r) const { return n * r.d != d * r.n; }
    bool operator<(const Fraction &r) const { return n * r.d < d * r.n; }
    bool operator>(const Fraction &r) const { return n * r.d > d * r.n; }
    bool operator<=(const Fraction &r) const { return n * r.d <= d * r.n; }
    bool operator>=(const Fraction &r) const { return n * r.d >= d * r.n; }
    Fraction& operator+=(const Fraction &r) { n = n * r.d + d * r.n; d *= r.d; return norm(); }
    Fraction& operator-=(const Fraction &r) { n = n * r.d - d * r.n; d *= r.d; return norm(); }
    Fraction& operator*=(const Fraction &r) { n *= r.n; d *= r.d; return norm(); }
    Fraction& operator/=(const Fraction &r) { n *= r.d; d *= r.n; return norm(); }
    Fraction operator+(const Fraction &r) const { return Fraction(*this) += r; }
    Fraction operator-(const Fraction &r) const { return Fraction(*this) -= r; }
    Fraction operator*(const Fraction &r) const { return Fraction(*this) *= r; }
    Fraction operator/(const Fraction &r) const { return Fraction(*this) /= r; }
};

using frac = Fraction<ll>;

int main() {
    int N = Cin(), S = Cin(), C = Cin();

    ll sumA = 0;
    vector<int> A(N), L(N), R(N);
    for (int i = 0; i < N; i++) {
        A[i] = Cin(), L[i] = Cin(), R[i] = Cin();
        sumA += A[i];
    }
    vector<frac> vw({ frac(-1), frac(1) });
    for (int i = 0; i < N; i++) {
        for (int j = i + 1; j < N; j++) {
            frac f(-L[i] + R[i] + L[j] - R[j], L[i] + R[i] - L[j] - R[j]);
            if (f >= frac(-1) && f <= frac(1)) vw.push_back(f);
        }
    }
    sort(vw.begin(), vw.end());
    vw.erase(unique(vw.begin(), vw.end()), vw.end());

    auto func = [&](frac w) -> frac {
        vector<pair<frac, int>> v;
        for (int i = 0; i < N; i++)
            v.emplace_back(w * frac(L[i] + R[i], 2) + frac(L[i] - R[i], 2), i);

        sort(v.begin(), v.end());
        vector<frac> y(N, frac(-1));
        frac z, limit = frac(sumA - S);
        for (auto [val, i] : v) {
            if (limit >= frac(0)) {
                limit -= frac(A[i]);
                y[i] = frac(0);
                if (limit < frac(0)) z = frac(0) - val;
            } else y[i] = z + val;
        }
        frac obj = z * frac(S) + w * frac(C) * frac(S);
        for (int i = 0; i < N; i++) obj -= y[i] * frac(A[i]);
        return obj;
    };

    int lo = 0, hi = vw.size() - 1;
    while (hi - lo > 2) {
        int m1 = (lo * 2 + hi) / 3, m2 = (lo + hi * 2) / 3;
        if (func(vw[m1]) > func(vw[m2])) hi = m2;
        else lo = m1;
    }
    frac ans = max({ func(vw[lo]), func(vw[(lo + hi) / 2]), func(vw[hi]) });
    ans /= 10000;
    Cout(ans.n), pc(' '), Cout(ans.d), pc('\n');
    return 0;
}
accuracy: 1
time_ms: 100
memory_kb: 13172
final_rank: 2

code_snippet: // AOJ #1464 Mixing Solutions
// 2025.3.28

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

#define gc() getchar_unlocked()
#define pc(c) putchar_unlocked(c)

int Cin() {
	int n = 0; int c = gc();
	do n = 10*n + (c & 0xf), c = gc(); while (c >= '0');
	return n;
}

void Cout(ll n) {
	char b[30];
	if (!n) pc('0');
	else {
		if (n < 0) pc('-'), n = -n;
		int i = 0; while (n) b[i++] = n % 10 + '0', n /= 10;
		while (i--) pc(b[i]);
	}
}

template <class T>
struct Fraction {
    T n, d;
    constexpr Fraction(T num = 0, T den = 1) : n(num), d(den) { norm(); }

    Fraction& norm() {
        if (d < 0) { n = -n; d = -d; }
        T g = std::gcd(n < 0 ? -n : n, d < 0 ? -d : d);
        if (g == 0) { n = 0; d = 1; }
        else { n /= g; d /= g; }
        return *this;
    }

    constexpr Fraction& operator=(T a) {
        *this = Fraction(a, 1);
        return *this;
    }

    constexpr bool operator==(const Fraction &r) const { return n * r.d == d * r.n; }
    constexpr bool operator!=(const Fraction &r) const { return n * r.d != d * r.n; }
    constexpr bool operator<(const Fraction &r) const { return n * r.d < d * r.n; }
    constexpr bool operator>(const Fraction &r) const { return n * r.d > d * r.n; }
    constexpr bool operator<=(const Fraction &r) const { return n * r.d <= d * r.n; }
    constexpr bool operator>=(const Fraction &r) const { return n * r.d >= d * r.n; }

    constexpr Fraction& operator+=(const Fraction &r) {
        n = n * r.d + d * r.n;
        d *= r.d;
        return norm();
    }
    constexpr Fraction& operator-=(const Fraction &r) {
        n = n * r.d - d * r.n;
        d *= r.d;
        return norm();
    }
    constexpr Fraction& operator*=(const Fraction &r) {
        n *= r.n;
        d *= r.d;
        return norm();
    }
    constexpr Fraction& operator/=(const Fraction &r) {
        n *= r.d;
        d *= r.n;
        return norm();
    }
    constexpr Fraction operator+(const Fraction &r) const { return Fraction(*this) += r; }
    constexpr Fraction operator-(const Fraction &r) const { return Fraction(*this) -= r; }
    constexpr Fraction operator*(const Fraction &r) const { return Fraction(*this) *= r; }
    constexpr Fraction operator/(const Fraction &r) const { return Fraction(*this) /= r; }
};

using frac = Fraction<ll>;

int main() {
    int N = Cin(), S = Cin(), C = Cin();

    ll sumA = 0;
    vector<ll> A(N), L(N), R(N);
    for (int i = 0; i < N; i++) {
        A[i] = Cin(), L[i] = Cin(), R[i] = Cin();
        sumA += A[i];
    }

    vector<frac> wvec({ frac(-1), frac(1) });
    for (int i = 0; i < N; i++) {
        for (int j = i + 1; j < N; j++) {
            frac f(-L[i] + R[i] + L[j] - R[j], L[i] + R[i] - L[j] - R[j]);
            if (f >= frac(-1) && f <= frac(1)) wvec.push_back(f);
        }
    }
    sort(wvec.begin(), wvec.end());
    wvec.erase(unique(wvec.begin(), wvec.end()), wvec.end());

    auto func = [&](frac w) -> frac {
        vector<pair<frac, int>> vec;
        for (int i = 0; i < N; i++) {
            frac v = w * frac(L[i] + R[i], 2) + frac(L[i] - R[i], 2);
            vec.emplace_back(v, i);
        }
        sort(vec.begin(), vec.end());
        vector<frac> y(N, frac(-1));
        frac z;
        frac lim(sumA - S, 1);
        for (auto [v, i] : vec) {
            if (lim >= frac(0)) {
                lim -= A[i];
                y[i] = frac(0);
                if (lim < frac(0)) z = frac(0) - v;
            } else y[i] = z + v;
        }
        frac obj = z * frac(S) + w * frac(C) * frac(S);
        for (int i = 0; i < N; i++) obj -= y[i] * frac(A[i]);
        return obj;
    };

    int l = 0, r = wvec.size() - 1;
    while (r - l > 2) {
        int m1 = (l * 2 + r) / 3, m2 = (l + r * 2) / 3;
        if (func(wvec[m1]) > func(wvec[m2])) r = m2;
        else l = m1;
    }
    frac res = max({ func(wvec[l]), func(wvec[(l + r) / 2]), func(wvec[r]) });
    res /= 10000;
    Cout(res.n), pc(' '), Cout(res.d), pc('\n');
    return 0;
}
accuracy: 1
time_ms: 100
memory_kb: 13260
final_rank: 3

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

// 有理数
template<class T> struct frac {
    // inner values
    T first, second;

    // constructor
    frac& normalize() {
        if (second < 0) first = -first, second = -second;
        T d = gcd(abs(first), abs(second));
        if (d == 0) first = 0, second = 1;
        else first /= d, second /= d;
        return *this;
    }
    constexpr frac(T f = 0, T s = 1) : first(f), second(s) { 
        normalize(); 
    }
    constexpr frac& operator = (T a) { 
        *this = frac(a, 1); 
        return *this;
    }

    // comparison operators
    constexpr bool operator == (const frac &r) const {
        return this->first == r.first && this->second == r.second;
    }
    constexpr bool operator != (const frac &r) const {
        return this->first != r.first || this->second != r.second;
    }
    constexpr bool operator < (const frac &r) const {
        return this->first * r.second < this->second * r.first;
    }
    constexpr bool operator > (const frac &r) const {
        return this->first * r.second > this->second * r.first;
    }
    constexpr bool operator <= (const frac &r) const {
        return this->first * r.second <= this->second * r.first;
    }
    constexpr bool operator >= (const frac &r) const {
        return this->first * r.second >= this->second * r.first;
    }
    
    // arithmetic operators
    constexpr frac& operator += (const frac &r) {
        this->first = this->first * r.second + this->second * r.first;
        this->second *= r.second;
        this->normalize();
        return *this;
    }
    constexpr frac& operator -= (const frac &r) {
        this->first = this->first * r.second - this->second * r.first;
        this->second *= r.second;
        this->normalize();
        return *this;
    }
    constexpr frac& operator *= (const frac &r) {
        this->first *= r.first;
        this->second *= r.second;
        this->normalize();
        return *this;
    }
    constexpr frac& operator /= (const frac &r) {
        this->first *= r.second;
        this->second *= r.first;
        this->normalize();
        return *this;
    }
    constexpr frac operator + () const { return frac(*this); }
    constexpr frac operator - () const { return frac(0) - frac(*this); }
    constexpr frac operator + (const frac &r) const { return frac(*this) += r; }
    constexpr frac operator - (const frac &r) const { return frac(*this) -= r; }
    constexpr frac operator * (const frac &r) const { return frac(*this) *= r; }
    constexpr frac operator / (const frac &r) const { return frac(*this) /= r; }
    friend constexpr ostream& operator << (ostream &os, const frac<T> &x) {
        os << x.first; 
        if (x.second != 1) os << "/" << x.second;
        return os;
    }
};


void ICPC_Asia_2024_J() {
    using lfrac = frac<long long>;
    const long long M = 10000;
    long long N, S, C, sumA = 0;
    cin >> N >> S >> C;
    vector<long long> A(N), L(N), R(N);
    for (int i = 0; i < N; i++) cin >> A[i] >> L[i] >> R[i], sumA += A[i];

    vector<lfrac> vw({lfrac(-1), lfrac(1)});
    for (int i = 0; i < N; i++) {
        for (int j = i+1; j < N; j++) {
            lfrac f(-L[i]+R[i]+L[j]-R[j], L[i]+R[i]-L[j]-R[j]);
            if (f >= -1 && f <= 1) vw.push_back(f);
        }
    }
    sort(vw.begin(), vw.end());
    vw.erase(unique(vw.begin(), vw.end()), vw.end());

    auto func = [&](lfrac w) -> lfrac { 
        vector<pair<lfrac,int>> v;
        for (int i = 0; i < N; i++) {
            v.emplace_back(w*lfrac(L[i]+R[i])/2 + lfrac(L[i]-R[i])/2, i);
        }
        sort(v.begin(), v.end());

        vector<lfrac> y(N, -1);
        lfrac z, limit = sumA - S;
        for (auto [val, i] : v) {
            if (limit >= 0) {
                limit -= A[i];
                y[i] = 0;
                if (limit < 0) z = -val;
            } else {
                y[i] = z + val;
            }
        }
        lfrac obj = z * S + w * C * S;
        for (int i = 0; i < N; i++) obj -= y[i] * A[i];
        return obj;
    };

    int left = 0, right = vw.size()-1;
    while (right - left > 2) {
        int m1 = (left * 2 + right) / 3, m2 = (left + right * 2) / 3;
        if (func(vw[m1]) > func(vw[m2])) right = m2;
        else left = m1;
    }
    lfrac res = max({func(vw[left]), func(vw[(left+right)/2]), func(vw[right])});
    res /= M;
    cout << res.first << " " << res.second << endl;
}


int main() {
    ICPC_Asia_2024_J();
}
accuracy: 1
time_ms: 100
memory_kb: 13296
final_rank: 4

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

#define DEBUG 1
#define COUT(x) if (DEBUG) cout << #x << " = " << (x) << " (L" << __LINE__ << ")" << endl
template<class T1, class T2> ostream& operator << (ostream &s, pair<T1,T2> P)
{ return s << '<' << P.first << ", " << P.second << '>'; }
template<class T> ostream& operator << (ostream &s, vector<T> P)
{ for (int i = 0; i < P.size(); ++i) { if (i > 0) { s << " "; } s << P[i]; } return s; }
template<class T> ostream& operator << (ostream &s, deque<T> P)
{ for (int i = 0; i < P.size(); ++i) { if (i > 0) { s << " "; } s << P[i]; } return s; }
template<class T> ostream& operator << (ostream &s, vector<vector<T> > P)
{ for (int i = 0; i < P.size(); ++i) { s << endl << P[i]; } return s << endl; }
template<class T> ostream& operator << (ostream &s, set<T> P)
{ for (auto it : P) { s << "<" << it << "> "; } return s; }
template<class T> ostream& operator << (ostream &s, multiset<T> P)
{ for (auto it : P) { s << "<" << it << "> "; } return s; }
template<class T1, class T2> ostream& operator << (ostream &s, map<T1,T2> P)
{ for (auto it : P) { s << "<" << it.first << "->" << it.second << "> "; } return s; }


// 有理数
template<class T> struct frac {
    // inner values
    T first, second;

    // constructor
    frac& normalize() {
        if (second < 0) first = -first, second = -second;
        T d = gcd(abs(first), abs(second));
        if (d == 0) first = 0, second = 1;
        else first /= d, second /= d;
        return *this;
    }
    constexpr frac(T f = 0, T s = 1) : first(f), second(s) { 
        normalize(); 
    }
    constexpr frac& operator = (T a) { 
        *this = frac(a, 1); 
        return *this;
    }

    // comparison operators
    constexpr bool operator == (const frac &r) const {
        return this->first == r.first && this->second == r.second;
    }
    constexpr bool operator != (const frac &r) const {
        return this->first != r.first || this->second != r.second;
    }
    constexpr bool operator < (const frac &r) const {
        return this->first * r.second < this->second * r.first;
    }
    constexpr bool operator > (const frac &r) const {
        return this->first * r.second > this->second * r.first;
    }
    constexpr bool operator <= (const frac &r) const {
        return this->first * r.second <= this->second * r.first;
    }
    constexpr bool operator >= (const frac &r) const {
        return this->first * r.second >= this->second * r.first;
    }
    
    // arithmetic operators
    constexpr frac& operator += (const frac &r) {
        this->first = this->first * r.second + this->second * r.first;
        this->second *= r.second;
        this->normalize();
        return *this;
    }
    constexpr frac& operator -= (const frac &r) {
        this->first = this->first * r.second - this->second * r.first;
        this->second *= r.second;
        this->normalize();
        return *this;
    }
    constexpr frac& operator *= (const frac &r) {
        this->first *= r.first;
        this->second *= r.second;
        this->normalize();
        return *this;
    }
    constexpr frac& operator /= (const frac &r) {
        this->first *= r.second;
        this->second *= r.first;
        this->normalize();
        return *this;
    }
    constexpr frac operator + () const { return frac(*this); }
    constexpr frac operator - () const { return frac(0) - frac(*this); }
    constexpr frac operator + (const frac &r) const { return frac(*this) += r; }
    constexpr frac operator - (const frac &r) const { return frac(*this) -= r; }
    constexpr frac operator * (const frac &r) const { return frac(*this) *= r; }
    constexpr frac operator / (const frac &r) const { return frac(*this) /= r; }
    friend constexpr ostream& operator << (ostream &os, const frac<T> &x) {
        os << x.first; 
        if (x.second != 1) os << "/" << x.second;
        return os;
    }
};


void ICPC_Asia_2024_J() {
    using lfrac = frac<long long>;
    const long long M = 10000;
    long long N, S, C, sumA = 0;
    cin >> N >> S >> C;
    vector<long long> A(N), L(N), R(N);
    for (int i = 0; i < N; i++) cin >> A[i] >> L[i] >> R[i], sumA += A[i];

    vector<lfrac> vw({lfrac(-1), lfrac(1)});
    for (int i = 0; i < N; i++) {
        for (int j = i+1; j < N; j++) {
            lfrac f(-L[i]+R[i]+L[j]-R[j], L[i]+R[i]-L[j]-R[j]);
            if (f >= -1 && f <= 1) vw.push_back(f);
        }
    }
    sort(vw.begin(), vw.end());
    vw.erase(unique(vw.begin(), vw.end()), vw.end());
    //COUT(vw);

    auto func = [&](lfrac w) -> lfrac { 
        vector<pair<lfrac,int>> v;
        for (int i = 0; i < N; i++) {
            v.emplace_back(w*lfrac(L[i]+R[i])/2 + lfrac(L[i]-R[i])/2, i);
        }
        sort(v.begin(), v.end());

        vector<lfrac> y(N, -1);
        lfrac z, limit = sumA - S;
        for (auto [val, i] : v) {
            if (limit >= 0) {
                limit -= A[i];
                y[i] = 0;
                if (limit < 0) z = -val;
            } else {
                y[i] = z + val;
            }
        }
        lfrac obj = z * S + w * C * S;
        for (int i = 0; i < N; i++) obj -= y[i] * A[i];

        //cout << "----" << endl; COUT(w); COUT(A); COUT(S); COUT(C*S); COUT(L); COUT(R); COUT(limit); COUT(v); COUT(y); COUT(z); COUT(w); COUT(obj);
        return obj;
    };

    int left = 0, right = vw.size()-1;
    while (right - left > 2) {
        //cout << left << ", " << right << endl;
        int m1 = (left * 2 + right) / 3, m2 = (left + right * 2) / 3;
        if (func(vw[m1]) > func(vw[m2])) right = m2;
        else left = m1;
    }
    // for (auto w : vw) {
    //     res = max(res, func(w)/M);
    // }
    lfrac res = max({func(vw[left]), func(vw[(left+right)/2]), func(vw[right])});
    res /= M;
    cout << res.first << " " << res.second << endl;
}


int main() {
    ICPC_Asia_2024_J();
}
accuracy: 1
time_ms: 100
memory_kb: 13308
final_rank: 5

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

#define DEBUG 1
#define COUT(x) if (DEBUG) cout << #x << " = " << (x) << " (L" << __LINE__ << ")" << endl
template<class T1, class T2> ostream& operator << (ostream &s, pair<T1,T2> P)
{ return s << '<' << P.first << ", " << P.second << '>'; }
template<class T> ostream& operator << (ostream &s, vector<T> P)
{ for (int i = 0; i < P.size(); ++i) { if (i > 0) { s << " "; } s << P[i]; } return s; }
template<class T> ostream& operator << (ostream &s, deque<T> P)
{ for (int i = 0; i < P.size(); ++i) { if (i > 0) { s << " "; } s << P[i]; } return s; }
template<class T> ostream& operator << (ostream &s, vector<vector<T> > P)
{ for (int i = 0; i < P.size(); ++i) { s << endl << P[i]; } return s << endl; }
template<class T> ostream& operator << (ostream &s, set<T> P)
{ for (auto it : P) { s << "<" << it << "> "; } return s; }
template<class T> ostream& operator << (ostream &s, multiset<T> P)
{ for (auto it : P) { s << "<" << it << "> "; } return s; }
template<class T1, class T2> ostream& operator << (ostream &s, map<T1,T2> P)
{ for (auto it : P) { s << "<" << it.first << "->" << it.second << "> "; } return s; }


// 有理数
template<class T> struct frac {
    // inner values
    T first, second;

    // constructor
    frac& normalize() {
        if (second < 0) first = -first, second = -second;
        T d = gcd(abs(first), abs(second));
        if (d == 0) first = 0, second = 1;
        else first /= d, second /= d;
        return *this;
    }
    constexpr frac(T f = 0, T s = 1) : first(f), second(s) { 
        normalize(); 
    }
    constexpr frac& operator = (T a) { 
        *this = frac(a, 1); 
        return *this;
    }

    // comparison operators
    constexpr bool operator == (const frac &r) const {
        return this->first == r.first && this->second == r.second;
    }
    constexpr bool operator != (const frac &r) const {
        return this->first != r.first || this->second != r.second;
    }
    constexpr bool operator < (const frac &r) const {
        return this->first * r.second < this->second * r.first;
    }
    constexpr bool operator > (const frac &r) const {
        return this->first * r.second > this->second * r.first;
    }
    constexpr bool operator <= (const frac &r) const {
        return this->first * r.second <= this->second * r.first;
    }
    constexpr bool operator >= (const frac &r) const {
        return this->first * r.second >= this->second * r.first;
    }
    
    // arithmetic operators
    constexpr frac& operator += (const frac &r) {
        this->first = this->first * r.second + this->second * r.first;
        this->second *= r.second;
        this->normalize();
        return *this;
    }
    constexpr frac& operator -= (const frac &r) {
        this->first = this->first * r.second - this->second * r.first;
        this->second *= r.second;
        this->normalize();
        return *this;
    }
    constexpr frac& operator *= (const frac &r) {
        this->first *= r.first;
        this->second *= r.second;
        this->normalize();
        return *this;
    }
    constexpr frac& operator /= (const frac &r) {
        this->first *= r.second;
        this->second *= r.first;
        this->normalize();
        return *this;
    }
    constexpr frac operator + () const { return frac(*this); }
    constexpr frac operator - () const { return frac(0) - frac(*this); }
    constexpr frac operator + (const frac &r) const { return frac(*this) += r; }
    constexpr frac operator - (const frac &r) const { return frac(*this) -= r; }
    constexpr frac operator * (const frac &r) const { return frac(*this) *= r; }
    constexpr frac operator / (const frac &r) const { return frac(*this) /= r; }
    friend constexpr ostream& operator << (ostream &os, const frac<T> &x) {
        os << x.first; 
        if (x.second != 1) os << "/" << x.second;
        return os;
    }
};


void ICPC_Asia_2024_J() {
    using lfrac = frac<long long>;
    const long long M = 10000;
    long long N, S, C, sumA = 0;
    cin >> N >> S >> C;
    vector<long long> A(N), L(N), R(N);
    for (int i = 0; i < N; i++) cin >> A[i] >> L[i] >> R[i], sumA += A[i];

    vector<lfrac> vw({lfrac(-1), lfrac(1)});
    for (int i = 0; i < N; i++) {
        for (int j = i+1; j < N; j++) {
            lfrac f(-L[i]+R[i]+L[j]-R[j], L[i]+R[i]-L[j]-R[j]);
            if (f >= -1 && f <= 1) vw.push_back(f);
        }
    }
    sort(vw.begin(), vw.end());
    //COUT(vw);

    auto func = [&](lfrac w) -> lfrac { 
        vector<pair<lfrac,int>> v;
        for (int i = 0; i < N; i++) {
            v.emplace_back(w*lfrac(L[i]+R[i])/2 + lfrac(L[i]-R[i])/2, i);
        }
        sort(v.begin(), v.end());

        vector<lfrac> y(N, -1);
        lfrac z, limit = sumA - S;
        for (auto [val, i] : v) {
            if (limit >= 0) {
                limit -= A[i];
                y[i] = 0;
                if (limit < 0) z = -val;
            } else {
                y[i] = z + val;
            }
        }
        lfrac obj = z * S + w * C * S;
        for (int i = 0; i < N; i++) obj -= y[i] * A[i];

        //cout << "----" << endl; COUT(w); COUT(A); COUT(S); COUT(C*S); COUT(L); COUT(R); COUT(limit); COUT(v); COUT(y); COUT(z); COUT(w); COUT(obj);
        return obj;
    };

    int left = 0, right = vw.size()-1;
    while (right - left > 2) {
        //cout << left << ", " << right << endl;
        int m1 = (left * 2 + right) / 3, m2 = (left + right * 2) / 3;
        if (func(vw[m1]) > func(vw[m2])) right = m2;
        else left = m1;
    }
    // for (auto w : vw) {
    //     res = max(res, func(w)/M);
    // }
    lfrac res = max({func(vw[left]), func(vw[(left+right)/2]), func(vw[right])});
    res /= M;
    cout << res.first << " " << res.second << endl;
}


int main() {
    ICPC_Asia_2024_J();
}
accuracy: 0.6428571428571429
time_ms: 90
memory_kb: 13292
final_rank: 8

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

using namespace std;
using ll = __int128_t;
ll abs(ll a) {
    return (a < 0 ? -a : a);
}
ll gcd(ll a, ll b) {
    while (a) {
        b %= a;
        swap(a, b);
    }
    return b;
}
class frac {
    public:
    ll mo;
    ll so;
    frac(ll b, ll a = 1) {
        if (b == 0 && a == 0) {
            mo = so = 1;
            return;
        }
	ll g = gcd(abs(a), abs(b));
	if (b == 0) {
	    g = abs(a);
	}
	a /= g; b /= g;
	if (a < 0) {
	    a *= -1;
	    b *= -1;
	}
	mo = a;
	so = b;
	assert(mo >= 0);
	assert(mo <= (ll)1e18);
	assert(abs(so) <= (ll)1e18);
    }
};
frac operator+(const frac& a, const frac& b) {
    ll g = gcd(a.mo, b.mo);
    ll a1 = a.mo / g, b1 = b.mo / g;
    return frac{a.so * b1 + b.so * a1, g * a1 * b1};
}
frac operator*(const frac& a, const frac& b) {
    return frac{a.so * b.so, a.mo * b.mo};
}
frac operator-(const frac& a, const frac& b) {
    return a + (b * -1);
}
frac operator/(const frac& a, const frac& b) {
    return frac{a.so * b.mo, a.mo * b.so};
}
bool operator<(const frac& a, const frac& b) {
    return a.so * b.mo < b.so * a.mo;
}
bool operator>(const frac& a, const frac& b) {
    return b < a;
}
bool operator==(const frac& a, const frac& b) {
    return (!(a < b)) && (!(b < a));
}
class segtree {
    public :
	vector<ll> data;
	int siz;
	segtree(const vector<ll>& X) {
	    int N = X.size();
	    siz = 1;
	    while (siz < N) {
		siz <<= 1;
	    }
	    data.assign(siz * 2, 0);
	    for (int i = 0; i < N; i ++) data[siz + i] = X[i];
	    for (int i = siz - 1; i; i --) {
		data[i] = data[i * 2 + 1] + data[i * 2];
	    }
	}
	void set(int id, ll x) {
	    id += siz;
	    data[id] = x;
	    id >>= 1;
	    while (id) {
		data[id] = data[id * 2] + data[id * 2 + 1];
		id >>= 1;
	    }
	}
	ll subcsum(int id, int a, int b, int l, int r) {
	    if (l <= a && b <= r) {
		return data[id];
	    }
	    if (r <= a || b <= l) {
		return 0;
	    }
	    int md = (a + b) / 2;
	    ll L = subcsum(id * 2,         a, md, l, r);
	    ll R = subcsum(id * 2 + 1, md, b, l, r);
	    return L+R;
	}
	ll csum(int l, int r) {
	    return subcsum(1, 0, siz, l, r);
	}
	// csum(0, x) < s になるような最大の x
	ll subMR(int id, int a, int b, ll s) {
	    if (b - a == 1) {
		int ret = id - siz;
		if (data[id] < s) ret ++;
		return ret;
	    }
	    int md = (a + b) / 2;
	    if (data[id * 2] >= s) {
		return subMR(id * 2, a, md, s);
	    }
	    s -= data[id * 2];
	    return subMR(id * 2 + 1, md, b, s);
	}
	ll MR(ll s) {
	    return subMR(1, 0, siz, s);
	}
};
int main () {
    int N;
    ll M = 10000;
    ll s, c;
    {
        int x, y;
        cin >> N >> x >> y;
        s = x; c = y;
    }
    vector<ll> A(N), L(N), R(N);
    for (int i = 0; i < N; i ++) {
        int a, l, r;
        cin >> a >> l >> r;
        A[i] = a; L[i] = l; R[i] = r;
    }
    vector<ll> D(N), E(N);
    for (int i = 0; i < N; i ++) {
	D[i] = R[i] - L[i];
	E[i] = R[i] + L[i];
    }
    segtree sg1(A), sg2(A), sg3(A);
    vector<int> ID(N);
    iota(ID.begin(), ID.end(), 0);
    sort(ID.begin(), ID.end(),
	    [&](int i, int j) {
	    return D[i] + E[i] < D[j] + E[j];
	    }
	);
    vector<int> IV(N);
    for (int i = 0; i < N; i ++) {
	IV[ID[i]] = i;
    }
    for (int i = 0; i < N; i ++) {
	int p = ID[i];
	sg1.set(i, A[p]);
	sg2.set(i, A[p] * D[p]);
	sg3.set(i, A[p] * E[p]);
    }
    auto calc = [&](frac q) -> frac {
        if(0) {
            for (int i = 1; i < N; i ++) {
                assert(!(D[ID[i-1]] - q * E[ID[i-1]] > D[ID[i]] - q * E[ID[i]]));
            }
        }
	int a = sg1.MR(s);
	frac x1 = q * (-E[ID[a]]) + D[ID[a]];
	frac ret = x1 * s;
	frac r2 = q * 2 * c * s;
	frac r3 = x1 * sg1.csum(0, a);
	frac r4 = sg2.csum(0, a) - q * sg3.csum(0, a);
	ret = ret/ (M * 2);
	r2 = r2 / (M * 2);
	r3 = r3 / (M * 2);
	r4 = r4 / (M * 2);
	if (0) {
	    printf("a : %d, ID[a] : %d\n", a, ID[a]);
	    printf("sg1 : %lld, sg2 : %lld, sg3 : %lld\n", sg1.csum(0, a), sg2.csum(0, a), sg3.csum(0, a));
	    printf("r1 : %lld/%lld\n", ret.so, ret.mo);
	    printf("r2 : %lld/%lld\n", r2.so, r2.mo);
	    printf("r3 : %lld/%lld\n", r3.so, r3.mo);
	    printf("r4 : %lld/%lld\n", r4.so, r4.mo);
	}
	return ret + r2 - r3 + r4;
    };
    // puts("DONE");
    // return 0;
    frac ans = calc(-1);
    using T = tuple<frac, frac, int>;
    vector<T>pts;
    // map<pair<frac, frac>, vector<int>> mp;
    for (int i = 0; i < N; i ++) {
	for (int j = i + 1; j < N; j ++) {
	    if (E[i] == E[j]) continue;
	    frac f(D[i] - D[j], E[i] - E[j]);
	    if ((!(f < -1)) && (!(1 < f))) {
		frac g = D[i] - f * E[i];
		pts.emplace_back(f, g, i);
		pts.emplace_back(f, g, j);
	    }
	}
    }
    // return 0;
    sort(pts.begin(), pts.end());
    for (int fll = 0; fll < pts.size(); fll ++) {
        auto& [q, hg, i] = pts[fll];
        int idl = IV[i], idr = IV[i] + 1;
        while (++fll < pts.size()) {
            auto& [q2, h2, i2] = pts[fll];
            if (!(q2 == q)) {
                fll --;
                break;
            }
            if (!(h2 == hg)) {
                fll --;
                break;
            }
            idl = min(idl, IV[i2]);
            idr = max(idr, IV[i2] + 1);
        }
        sort(ID.begin() + idl, ID.begin() + idr, [&](int i, int j){return E[i] > E[j];});
        for (int i = idl; i < idr; i ++) {
            int p = ID[i];
            IV[p] = i;
            sg1.set(i, A[p]);
            sg2.set(i, A[p] * D[p]);
            sg3.set(i, A[p] * E[p]);
        }
        ans = max(ans, calc(q));
    }
    ans = max(ans, calc(1));
    cout << (long long)ans.so << " " << (long long)ans.mo << endl;
}
accuracy: 1
time_ms: 1810
memory_kb: 87292
final_rank: 7

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

using namespace std;
using ll = __int128_t;
ll abs(ll a) {
    return (a < 0 ? -a : a);
}
ll gcd(ll a, ll b) {
    while (a) {
        b %= a;
        swap(a, b);
    }
    return b;
}
class frac {
    public:
    ll mo;
    ll so;
    frac(ll b, ll a = 1) {
	ll g = gcd(abs(a), abs(b));
	if (b == 0) g = abs(a);
	a /= g; b /= g;
	if (a < 0) {
	    a *= -1;
	    b *= -1;
	}
	mo = a;
	so = b;
    }
};
frac operator+(const frac& a, const frac& b) {
    ll g = gcd(a.mo, b.mo);
    ll a1 = a.mo / g, b1 = b.mo / g;
    return frac{a.so * b1 + b.so * a1, g * a1 * b1};
}
frac operator*(const frac& a, const frac& b) {
    return frac{a.so * b.so, a.mo * b.mo};
}
frac operator-(const frac& a, const frac& b) {
    return a + (b * -1);
}
frac operator/(const frac& a, const frac& b) {
    return frac{a.so * b.mo, a.mo * b.so};
}
bool operator<(const frac& a, const frac& b) {
    return a.so * b.mo < b.so * a.mo;
}
bool operator>(const frac& a, const frac& b) {
    return b < a;
}
bool operator==(const frac& a, const frac& b) {
    return (!(a < b)) && (!(b < a));
}
class segtree {
    public :
	vector<ll> data;
	int siz;
	segtree(const vector<ll>& X) {
	    int N = X.size();
	    siz = 1;
	    while (siz < N) {
		siz <<= 1;
	    }
	    data.assign(siz * 2, 0);
	    for (int i = 0; i < N; i ++) data[siz + i] = X[i];
	    for (int i = siz - 1; i; i --) {
		data[i] = data[i * 2 + 1] + data[i * 2];
	    }
	}
	void set(int id, ll x) {
	    id += siz;
	    data[id] = x;
	    id >>= 1;
	    while (id) {
		data[id] = data[id * 2] + data[id * 2 + 1];
		id >>= 1;
	    }
	}
	ll subcsum(int id, int a, int b, int l, int r) {
	    if (l <= a && b <= r) {
		return data[id];
	    }
	    if (r <= a || b <= l) {
		return 0;
	    }
	    int md = (a + b) / 2;
	    ll L = subcsum(id * 2,         a, md, l, r);
	    ll R = subcsum(id * 2 + 1, md, b, l, r);
	    return L+R;
	}
	ll csum(int l, int r) {
	    return subcsum(1, 0, siz, l, r);
	}
	// csum(0, x) < s になるような最大の x
	ll subMR(int id, int a, int b, ll s) {
	    if (b - a == 1) {
		int ret = id - siz;
		if (data[id] < s) ret ++;
		return ret;
	    }
	    int md = (a + b) / 2;
	    if (data[id * 2] >= s) {
		return subMR(id * 2, a, md, s);
	    }
	    s -= data[id * 2];
	    return subMR(id * 2 + 1, md, b, s);
	}
	ll MR(ll s) {
	    return subMR(1, 0, siz, s);
	}
};
int main () {
    int N;
    ll M = 10000;
    ll s, c;
    {
        int x, y;
        cin >> N >> x >> y;
        s = x; c = y;
    }
    vector<ll> A(N), L(N), R(N);
    for (int i = 0; i < N; i ++) {
        int a, l, r;
        cin >> a >> l >> r;
        A[i] = a; L[i] = l; R[i] = r;
    }
    vector<ll> D(N), E(N);
    for (int i = 0; i < N; i ++) {
	D[i] = R[i] - L[i];
	E[i] = R[i] + L[i];
    }
    segtree sg1(A), sg2(A), sg3(A);
    vector<int> ID(N);
    iota(ID.begin(), ID.end(), 0);
    sort(ID.begin(), ID.end(),
	    [&](int i, int j) {
	    return D[i] + E[i] < D[j] + E[j];
	    }
	);
    vector<int> IV(N);
    for (int i = 0; i < N; i ++) {
	IV[ID[i]] = i;
    }
    for (int i = 0; i < N; i ++) {
	int p = ID[i];
	sg1.set(i, A[p]);
	sg2.set(i, A[p] * D[p]);
	sg3.set(i, A[p] * E[p]);
    }
    auto calc = [&](frac q) -> frac {
	int a = sg1.MR(s);
	frac x1 = q * (-E[ID[a]]) + D[ID[a]];
	frac ret = x1 * s;
	frac r2 = q * 2 * c * s;
	frac r3 = x1 * sg1.csum(0, a);
	frac r4 = sg2.csum(0, a) - q * sg3.csum(0, a);
	ret = ret/ (M * 2);
	r2 = r2 / (M * 2);
	r3 = r3 / (M * 2);
	r4 = r4 / (M * 2);
	if (0) {
	    printf("a : %d, ID[a] : %d\n", a, ID[a]);
	    printf("sg1 : %lld, sg2 : %lld, sg3 : %lld\n", sg1.csum(0, a), sg2.csum(0, a), sg3.csum(0, a));
	    printf("r1 : %lld/%lld\n", ret.so, ret.mo);
	    printf("r2 : %lld/%lld\n", r2.so, r2.mo);
	    printf("r3 : %lld/%lld\n", r3.so, r3.mo);
	    printf("r4 : %lld/%lld\n", r4.so, r4.mo);
	}
	return ret + r2 - r3 + r4;
    };
    // puts("DONE");
    // return 0;
    frac ans = calc(-1);
    using T = tuple<frac, int, int>;
    vector<T>pts;
    for (int i = 0; i < N; i ++) {
	for (int j = i + 1; j < N; j ++) {
	    frac f(D[i] - D[j], E[i] - E[j]);
	    if (-1 < f && f < 1) {
		pts.emplace_back(f, i, j);
	    }
	}
    }
    // return 0;
    sort(pts.begin(), pts.end());
    for (auto& [f, i, j] : pts) {
	// cout << "f : " << f.so << "/" << f.mo << endl;
	// cout << "a : "<< ans.so << "/" << ans.mo << endl;
	int idi = IV[i], idj = IV[j];
	sg1.set(idi, A[j]);
	sg2.set(idi, A[j] * D[j]);
	sg3.set(idi, A[j] * E[j]);
	sg1.set(idj, A[i]);
	sg2.set(idj, A[i] * D[i]);
	sg3.set(idj, A[i] * E[i]);
	swap(ID[idi], ID[idj]);
	swap(IV[i], IV[j]);
	ans = max(ans, calc(f));
    }
    ans = max(ans, calc(1));
    cout << (long long)ans.so << " " << (long long)ans.mo << endl;
}
accuracy: 0.2
time_ms: 1040
memory_kb: 27940
final_rank: 9

code_snippet: #include <bits/stdc++.h>
#define ff first
#define ss second
using namespace std;

typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;

struct point {
	int x, y;
};
point operator+(point a, point b) { return {a.x + b.x, a.y + b.y}; }
point operator-(point a, point b) { return {a.x - b.x, a.y - b.y}; }
ll operator*(point a, point b) { return 1ll * a.x * b.x + 1ll * a.y * b.y; }
ll operator/(point a, point b) { return 1ll * a.x * b.y - 1ll * a.y * b.x; }

int main() {
	cin.tie(0); ios_base::sync_with_stdio(0);
	map<pii, int> mp;

	int n, s, c;
	cin >> n >> s >> c;
	ll cs = 1ll * s * c;

	for (int i = 0; i < n; i++) {
		int w, x, y;
		cin >> w >> x >> y;
		mp[{x, y}] += w;
	}
	n = mp.size();

	int x[n], y[n], a[n], sp = 0;
	for (auto [pr, w] : mp) {
		auto [x_, y_] = pr;
		x[sp] = x_;
		y[sp] = y_;
		a[sp] = w;
		++sp;
	}

	vector<pair<point, pii>> vec;
	for (int i = 0; i < n; i++) for (int j = i + 1; j < n; j++) {
		vec.push_back({point{y[i] - y[j], x[j] - x[i]}, pii{i, j}});
	}
	sort(vec.begin(), vec.end(), [&](auto p, auto q) {
		if (p.ff / q.ff != 0) {
			return p.ff / q.ff > 0;
		}
		else return p.ss < q.ss;
	});

	int ord[n], rnk[n];
	iota(ord, ord + n, 0);
	iota(rnk, rnk + n, 0);

	ll pre[n + 1]{}, preX[n + 1]{}, preY[n + 1]{};
	for (int i = 0; i < n; i++) {
		pre[i + 1] = pre[i] + a[i];
		preX[i + 1] = preX[i] + 1ll * a[i] * x[i];
		preY[i + 1] = preY[i] + 1ll * a[i] * y[i];
	}

	vector<pll> A;
	auto calc = [&]() {
		int k = lower_bound(pre, pre + n + 1, s) - pre;
		ll xsum = preX[k - 1] + 1ll * (s - pre[k - 1]) * x[ord[k - 1]];
		ll ysum = preY[k - 1] + 1ll * (s - pre[k - 1]) * y[ord[k - 1]];
		if (A.empty() || A.back().ff != xsum) {
			A.push_back({xsum, ysum});
		}
	};

	calc();
	for (auto [t, pr] : vec) {
		auto [u, v] = pr;
		
		assert(rnk[u] + 1 == rnk[v]);
		swap(ord[rnk[u]], ord[rnk[v]]);
		swap(rnk[u], rnk[v]);

		int k = rnk[v];
		pre[k + 1] = pre[k] + a[v];
		preX[k + 1] = preX[k] + 1ll * a[v] * x[v];
		preY[k + 1] = preY[k] + 1ll * a[v] * y[v];
		calc();
	}

	ll p = 1e18, q = 1;
	for (auto [x, y] : A) {
		ll mx = max(abs(cs - x), abs(cs - y));
		if (mx < p) {
			p = mx;
		}
	}
	int sz = A.size();
	for (int i = 1; i < sz; i++) {
		auto [x0, y0] = A[i - 1];
		auto [x1, y1] = A[i];

		ll u = (x1 - x0) * 2 * cs + x0 * (y1 - y0) - y0 * (x1 - x0);
		ll d = x1 - x0 + y1 - y0;
		if (d < 0) {
			d *= -1;
			u *= -1;
		}

		if (d == 0) continue;
		if (d * x0 <= u && u <= d * x1) {
			u -= cs * d;
			if (u < 0) u *= -1;

			if ((__int128)p * d > (__int128)u * q) {
				p = u;
				q = d;
			}
		}
	}
	q *= 10000;
	ll g = __gcd(p, q);
	p /= g;
	q /= g;
	cout << p << ' ' << q << '\n';
}
accuracy: 1
time_ms: 60
memory_kb: 13468
final_rank: 1