use num::bigint::BigInt; use num::Zero; use std::cmp::Ordering; use std::collections::BinaryHeap; use std::io::{self, Read, Write}; use std::str::FromStr; const MAX_AB: usize = 300; struct RawEdge { u: usize, v: usize, numerator: usize, denominator: usize, } struct Edge { to: usize, weight: BigInt, } #[derive(Eq, PartialEq)] struct State { distance: BigInt, vertex: usize, } // RustのBinaryHeapは最大ヒープなので、比較を逆転させて最小ヒープにする。 impl Ord for State { fn cmp(&self, other: &Self) -> Ordering { other .distance .cmp(&self.distance) .then_with(|| other.vertex.cmp(&self.vertex)) } } impl PartialOrd for State { fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } } struct Scanner { input: Vec, index: usize, } impl Scanner { fn new() -> Self { let mut input = String::new(); io::stdin().read_to_string(&mut input).unwrap(); Self { input: input.into_bytes(), index: 0, } } fn next(&mut self) -> T { while self.index < self.input.len() && self.input[self.index].is_ascii_whitespace() { self.index += 1; } let start = self.index; while self.index < self.input.len() && !self.input[self.index].is_ascii_whitespace() { self.index += 1; } std::str::from_utf8(&self.input[start..self.index]) .unwrap() .parse() .ok() .unwrap() } } fn main() { let mut scanner = Scanner::new(); let n: usize = scanner.next(); let m: usize = scanner.next(); let mut raw_edges = Vec::with_capacity(m); // maximum_exponent[p]は、いずれかのb_iに現れるpの指数の最大値。 let mut maximum_exponent = vec![0usize; MAX_AB + 1]; for _ in 0..m { let mut u: usize = scanner.next(); let mut v: usize = scanner.next(); let a: usize = scanner.next(); let b: usize = scanner.next(); u -= 1; v -= 1; raw_edges.push(RawEdge { u, v, numerator: a, denominator: b, }); let mut value = b; let mut prime = 2; while prime * prime <= value { if value % prime != 0 { prime += 1; continue; } let mut exponent = 0; while value % prime == 0 { value /= prime; exponent += 1; } maximum_exponent[prime] = maximum_exponent[prime].max(exponent); prime += 1; } if value > 1 { maximum_exponent[value] = maximum_exponent[value].max(1); } } // すべてのb_iを割り切る共通分母を構築する。 let mut common_denominator = BigInt::from(1usize); let mut prime_powers: Vec<(BigInt, usize)> = Vec::new(); for prime in 2..=MAX_AB { let exponent = maximum_exponent[prime]; if exponent == 0 { continue; } let prime_bigint = BigInt::from(prime); prime_powers.push((prime_bigint.clone(), exponent)); for _ in 0..exponent { common_denominator *= &prime_bigint; } } let mut graph: Vec> = (0..n).map(|_| Vec::new()).collect(); for raw in raw_edges { let mut scaled_weight = common_denominator.clone(); scaled_weight /= raw.denominator; scaled_weight *= raw.numerator; // 無向辺なので、一方では複製し、もう一方では所有権を移動する。 graph[raw.u].push(Edge { to: raw.v, weight: scaled_weight.clone(), }); graph[raw.v].push(Edge { to: raw.u, weight: scaled_weight, }); } let mut distance = vec![BigInt::zero(); n]; let mut reached = vec![false; n]; let mut queue = BinaryHeap::new(); reached[0] = true; distance[0] = BigInt::zero(); queue.push(State { distance: BigInt::zero(), vertex: 0, }); while let Some(current) = queue.pop() { if !reached[current.vertex] || current.distance != distance[current.vertex] { continue; } for edge in &graph[current.vertex] { // 多倍長整数の加算を一度だけ行う。 let next_distance = ¤t.distance + &edge.weight; if !reached[edge.to] || next_distance < distance[edge.to] { reached[edge.to] = true; distance[edge.to] = next_distance.clone(); queue.push(State { distance: next_distance, vertex: edge.to, }); } } } // 出力全体をStringに格納してから一度に書き込む。 let mut output = String::with_capacity(8 * 1024 * 1024); for vertex in 1..n { let mut numerator = distance[vertex].clone(); let mut denominator = common_denominator.clone(); // 分母の素因数分解は既知なので、各素数で約分する。 for (prime, exponent) in &prime_powers { for _ in 0..*exponent { if !(&numerator % prime).is_zero() { break; } numerator /= prime; denominator /= prime; } } output.push_str(&numerator.to_string()); output.push(' '); output.push_str(&denominator.to_string()); output.push('\n'); } let stdout = io::stdout(); let mut writer = io::BufWriter::new(stdout.lock()); writer.write_all(output.as_bytes()).unwrap(); }