//CAUTION : this template is only suitable with C++17 (above) and home training, do not abuse when practicing offline
#include <bits/stdc++.h>
using namespace std;
//Benq inspires me lol
#define tcT template<class T
#define tcTU tcT, class U
//pairs
#define mp make_pair
#define mt make_tuple
#define ff first
#define ss second
//vectors
#define sz(v) (int)v.size()
#define all(v) begin(v), end(v)
#define rall(v) rbegin(v), rend(v)
#define pb push_back
#define eb emplace_back
#define compact(v) v.erase(unique(all(v)), end(v))
tcT> int lwb(const vector<T>& a, const T& b){ return int(lower_bound(all(a), b) - begin(a)); }
tcT> int upb(const vector<T>& a, const T& b){ return int(upper_bound(all(a), b) - begin(a)); }
//loops (warning : ONLY for int)
#define rep(i, l, r) for(int i = (l); i < (r); ++i)
#define FOR(i, l, r) for(int i = (l); i <= (r); ++i)
//debug
#define dbg(x) "[" #x " = " << (x) << "]"
//data types
using ll = long long;
using db = double;
using ld = long double;
using pi = pair<int, int>;
using pl = pair<ll, ll>;
using vi = vector<int>;
using vl = vector<ll>;
using vb = vector<bool>;
using vd = vector<db>;
using vc = vector<char>;
using vstr = vector<string>;
using vpi = vector<pi>;
using vpl = vector<pl>;
tcT> using min_heap = priority_queue<T, vector<T>, greater<T>>;
tcT> using max_heap = priority_queue<T>;
//bitwise operations
#define popcount(x) __builtin_popcountll(x) //count bits
#define BIT(k) (1LL << k) //bit k-th
#define all_bits(k) ((1LL << k) - 1) //first k bits on
//functions
tcT> bool minimize(T& a, const T& b){ if(a > b) return a = b, true; return false; } 
tcT> bool maximize(T& a, const T& b){ if(a < b) return a = b, true; return false; } 
tcT> T ceil_div(T a, T b){ return (a / b) + ((a ^ b) > 0 && a % b); }
tcT> T floor_div(T a, T b){ return (a / b) - ((a ^ b) < 0 && a % b); }
tcTU> T first_true(T lo, T hi, U f){ //if no exist then return hi+1
    ++hi; assert(lo <= hi); 
    while(lo < hi){ //[lo, hi)
        T mid = (lo + hi) >> 1;
        f(mid) ? hi = mid : lo = mid + 1;
    }
    return lo;
}
tcTU> T last_true(T lo, T hi, U f){ //if no exist then return lo-1
    --lo; assert(lo <= hi); 
    while(lo < hi){ //(lo, hi]
        T mid = (lo + hi) >> 1;
        f(mid) ? lo = mid : hi = mid - 1;
    }
    return lo;
}
#ifdef LOCAL //for checking time elapsed
const auto start_time = std::chrono::high_resolution_clock::now();
db time_elapsed(){ return chrono::duration<db>(std::chrono::high_resolution_clock::now() - 
                                               start_time).count(); }
#endif //LOCAL
void setIO(){
    // ios_base::sync_with_stdio(0); cin.tie(0);
#define task "task"
    if(fopen(task".inp", "r")){
        freopen(task".inp", "r", stdin);
        // freopen(task".out", "w", stdout);
    }
}
const int MAX = 1e4 + 5;
struct edge{
    int u, v, t, c, prior;
    edge() : u(), v(), t(), c(), prior() {}
    edge(int u, int v, int t, int c, int prior) : u(u), v(v), t(t), c(c), prior(prior) {}
    bool operator < (const edge& o) const {
        return prior < o.prior;
    }
} edges[MAX];
int N, M, lab[MAX];
ll ans = LLONG_MAX, ans_t, ans_c;
vpi ans_edges;
bool mark[MAX];
int root(int u){
    return lab[u] < 0 ? u : (lab[u] = root(lab[u]));
}
bool join(int u, int v){
    u = root(u); v = root(v);
    if(u == v) return false;
    if(lab[u] > lab[v]) swap(u, v);
    lab[u] += lab[v]; lab[v] = u;
    return true;
}
pair<int, int> solve_ratio(int a, int b){ //at + bc
    // cout << "input :" << a << ' ' << b << '\n';
    rep(i, 0, M){
        edges[i].prior = a * edges[i].t + b * edges[i].c;
        mark[i] = false;
    }
    sort(edges, edges + M);
    fill(lab, lab + N, -1);
    int comps = N, sum_t = 0, sum_c = 0;
    rep(i, 0, M){
        if(join(edges[i].u, edges[i].v)){
            --comps;
            sum_t += edges[i].t;
            sum_c += edges[i].c;
            mark[i] = true;
        }
        if(comps == 1) break;
    }
    // cout << "output : " << sum_t << ' ' << sum_c << "\n\n";
    if(1LL * sum_t * sum_c < ans){
        ans = 1LL * sum_t * sum_c;
        ans_t = sum_t;
        ans_c = sum_c;
        ans_edges.clear();
        rep(i, 0, M){
            if(mark[i]){
                ans_edges.pb(mp(edges[i].u, edges[i].v));
            }
        }
    }
    return mp(sum_t, sum_c);
}
// int called = 0;
// tcTU>
// ostream& operator << (ostream& stream, const pair<T, U>& v){
//     return stream << "(" << v.ff << ' ' << v.ss << ")";
// }
void solve(const pair<int, int>& L, const pair<int, int>& R){
    pair<int, int> M = solve_ratio(R.ss - L.ss, L.ff - R.ff);
    if(L == M || M == R) return;
    //we receive the point (sum_t, sum_c) that lies on the line y = ax + b where a = (R.c - L.c) / (L.t - R.t)
    // cout << L << ' ' << M << ' ' << R << '\n';
    // ++called;
    solve(L, M);
    solve(M, R);
}
void testcase(int n_case){
    cin >> N >> M;
    rep(i, 0, M){
        int u, v, t, c;
        cin >> u >> v >> t >> c;
        edges[i] = edge(u, v, t, c, 0);
    }
    solve(solve_ratio(0, 1), solve_ratio(1, 0));
    cout << ans_t << ' ' << ans_c << '\n';
    for(auto [u, v] : ans_edges) cout << u << ' ' << v << '\n';
}
int main(){
    setIO();
    int T = 1; 
    //cin >> T;
    rep(i, 0, T) testcase(i);
#ifdef LOCAL
    cerr << '\n' << "Execution time : " << (time_elapsed() * 1000.0) << " ms";
#endif //LOCAL
    return 0;
}
Compilation message (stderr)
timeismoney.cpp: In function 'void setIO()':
timeismoney.cpp:95:16: warning: ignoring return value of 'FILE* freopen(const char*, const char*, FILE*)' declared with attribute 'warn_unused_result' [-Wunused-result]
   95 |         freopen(task".inp", "r", stdin);
      |         ~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~| # | Verdict | Execution time | Memory | Grader output | 
|---|
| Fetching results... |