Problem:
MathJax.Hub.Config({ tex2jax: { inlineMath: [["$","$"], ["\\(","\\)"]], processEscapes: true }});

Problem E: Tangled Cables

Problem

とある会社のネットワーク上には$n$台のコンピュータと、それらをつなぐ$m$本の通信ケーブルがある。コンピュータは$0$から$n - 1$までの識別子で区別され、通信ケーブルもまた$0$から$m - 1$までの識別子をもつ。

現在この会社にある任意の異なる2台のコンピュータは、いくつかの通信ケーブルを介して相互に通信することができるが、通信経路がただ一つに定まるとは限らない。

このネットワークには、通信ケーブルが多すぎて絡まってしまうという問題がある。そこであなたは、任意の通信について通信経路がただ一つになるようにいくつかの通信ケーブルを取り除くことにした。

通信ケーブル$i$はコンピュータ$a_i$と$b_i$を双方向につなぎ、その長さは$c_i$である。また、あなたが通信ケーブル$i$を取り除く際にかかる労力は$d_i$である。

作業を始める前に、あなたは取り除く通信ケーブルの長さの和に対する、作業を終えるまでにかかる労力の和の比を「おっくう感」とし、最小のおっくう感を見積もることにした。

ただし、一つも取り除けない場合のおっくう感は$0$である。

Input

入力は以下の形式で与えられる。

$n$ $m$

$a_1$ $b_1$ $c_1$ $d_1$

$a_2$ $b_2$ $c_2$ $d_2$

...

$a_m$ $b_m$ $c_m$ $d_m$

入力はすべて整数で与えられる。

1行目にはコンピュータの数$n$と通信ケーブルの数$m$が空白区切りで与えられる。

2行目以降の$m$行には、通信ケーブルの情報が空白区切りで与えられる。$i$番目の通信ケーブルの情報は、通信ケーブル$i$の情報を表す。

Constraints

入力は以下の条件を満たす。

$2 \leq n \leq 10^4$

$n-1 \leq m \leq min(\frac{n \times (n - 1)}{2}, 10^4)$

$0 \leq a_i, b_i \leq n - 1 (a_i ≠ b_i)$

$1 \leq c_i \leq 10^6$

$0 \leq d_i \leq 10^6$

Output

おっくう感の最小値を実数で1行に出力する。ただし、$10^{-5}$を超える誤差を含んではならない。

Sample Input 1

4 5
0 2 22 13
0 3 15 25
1 2 3 3
1 3 28 5
2 3 5 22

Sample Output 1

0.36000

通信ケーブル$0$と通信ケーブル$3$を取り除くと、おっくう感は$(\frac{13 + 5}{22 + 28}) = 0.36$となり、これがおっくう感の最小値となる。

Sample Input 2

5 6
0 1 22 33
0 2 43 11
0 3 92 10
1 3 50 12
2 3 88 2
3 4 58 89

Sample Output 2

0.06667

Sample Input 3

2 1
0 1 1 1

Sample Output 3

0

Solution in JAVA (judge verdict: Wrong Answer)

import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.Comparator;
import java.util.NoSuchElementException;

public class Main implements Runnable {

	int N, M;
	long C, D;
	int[] union;
	Edge[] edges;

	private class Edge implements Comparable<Edge> {
		int from, to, c, d;

		public Edge(int from, int to, int c, int d) {
			this.from = from;
			this.to = to;
			this.c = c;
			this.d = d;
		}

		public int compareTo(Edge other) {
			double a = this.d * 1.0 / this.c;
			double b = other.d * 1.0 / other.c;

			if (a < b) {
				return 1;
			} else if (a > b) {
				return -1;
			}
			return 0;
		}
	}

	public boolean same(int x, int y) {
		return find(x) == find(y);
	}

	public int find(int x) {
		return union[x] = (union[x] == x ? x : find(union[x]));
	}

	public void unite(int x, int y) {
		x = find(x);
		y = find(y);

		if (same(x, y))
			return;

		if (x < y) {
			union[y] = x;
		} else {
			union[x] = y;
		}
	}

	public void solve() {
		N = nextInt();
		M = nextInt();

		C = 0;
		D = 0;

		edges = new Edge[M];
		for (int i = 0; i < M; i++) {
			int a = nextInt();
			int b = nextInt();
			int c = nextInt();
			int d = nextInt();
			edges[i] = new Edge(a, b, c, d);
			C += c;
			D += d;
		}
		union = new int[N];

		double ans = 0;
		double low = 0;
		double high = 100000;
		for(int x = 0;x < 100;x++){
			double mid = (high+low)/2;

			Arrays.sort(edges,new Comparator<Edge>(){
				public int compare(Edge e1, Edge e2) {
					double a = e1.d - e1.c * mid;
					double b = e2.d - e2.c * mid;

					if(a < b){
						return 1;
					}else if(a > b){
						return -1;
					}
					return 0;
				}
			});

			for (int i = 0; i < N; i++) {
				union[i] = i;
			}

			long takeC = 0;
			long takeD = 0;

			for (int i = 0; i < M; i++) {
				Edge e = edges[i];

				if (!same(e.from, e.to)) {
					unite(e.from, e.to);
					takeC += e.c;
					takeD += e.d;
				}
			}
			double sum = (D-takeD) * 1.0 / (C - takeC);
			if(C - takeC == 0)sum = 0;

			if(sum >= ans){
				ans = sum;
				low = mid;
			}else{
				high = mid;
			}
		}

		out.println(ans);
	}

	public static void main(String[] args) {
		new Thread(null, new Main(), "", 64 * 1024 * 1024).start();
	}

	/* Input */
	private static final InputStream in = System.in;
	private static final PrintWriter out = new PrintWriter(System.out);
	private final byte[] buffer = new byte[2048];
	private int p = 0;
	private int buflen = 0;

	private boolean hasNextByte() {
		if (p < buflen)
			return true;
		p = 0;
		try {
			buflen = in.read(buffer);
		} catch (IOException e) {
			e.printStackTrace();
		}
		if (buflen <= 0)
			return false;
		return true;
	}

	public boolean hasNext() {
		while (hasNextByte() && !isPrint(buffer[p])) {
			p++;
		}
		return hasNextByte();
	}

	private boolean isPrint(int ch) {
		if (ch >= '!' && ch <= '~')
			return true;
		return false;
	}

	private int nextByte() {
		if (!hasNextByte())
			return -1;
		return buffer[p++];
	}

	public String next() {
		if (!hasNext())
			throw new NoSuchElementException();
		StringBuilder sb = new StringBuilder();
		int b = -1;
		while (isPrint((b = nextByte()))) {
			sb.appendCodePoint(b);
		}
		return sb.toString();
	}

	public int nextInt() {
		return Integer.parseInt(next());
	}

	public long nextLong() {
		return Long.parseLong(next());
	}

	public double nextDouble() {
		return Double.parseDouble(next());
	}

	@Override
	public void run() {
		out.flush();
		new Main().solve();
		out.close();

	}
}