package main import ( "bytes" "container/heap" "io" "math/big" "os" ) const MaxAB = 300 type RawEdge struct { u int v int numerator int denominator int } type Edge struct { to int weight *big.Int } type State struct { distance *big.Int vertex int } type PriorityQueue []State func (pq PriorityQueue) Len() int { return len(pq) } func (pq PriorityQueue) Less(i, j int) bool { comparison := pq[i].distance.Cmp(pq[j].distance) if comparison != 0 { return comparison < 0 } return pq[i].vertex < pq[j].vertex } func (pq PriorityQueue) Swap(i, j int) { pq[i], pq[j] = pq[j], pq[i] } func (pq *PriorityQueue) Push(value interface{}) { *pq = append(*pq, value.(State)) } func (pq *PriorityQueue) Pop() interface{} { old := *pq lastIndex := len(old) - 1 result := old[lastIndex] old[lastIndex] = State{} *pq = old[:lastIndex] return result } type PrimePower struct { prime int exponent int } type FastScanner struct { data []byte index int } func NewFastScanner() *FastScanner { data, err := io.ReadAll(os.Stdin) if err != nil { panic(err) } return &FastScanner{ data: data, } } func (scanner *FastScanner) NextInt() int { for scanner.index < len(scanner.data) && scanner.data[scanner.index] <= ' ' { scanner.index++ } sign := 1 if scanner.data[scanner.index] == '-' { sign = -1 scanner.index++ } value := 0 for scanner.index < len(scanner.data) { c := scanner.data[scanner.index] if c < '0' || c > '9' { break } value = value*10 + int(c-'0') scanner.index++ } return value * sign } func main() { scanner := NewFastScanner() n := scanner.NextInt() m := scanner.NextInt() rawEdges := make([]RawEdge, 0, m) // maximumExponent[p]は、いずれかのb_iに現れる // 素数pの指数の最大値。 maximumExponent := make([]int, MaxAB+1) for i := 0; i < m; i++ { u := scanner.NextInt() - 1 v := scanner.NextInt() - 1 a := scanner.NextInt() b := scanner.NextInt() rawEdges = append(rawEdges, RawEdge{ u: u, v: v, numerator: a, denominator: b, }) value := b for prime := 2; prime*prime <= value; prime++ { if value%prime != 0 { continue } exponent := 0 for value%prime == 0 { value /= prime exponent++ } if exponent > maximumExponent[prime] { maximumExponent[prime] = exponent } } if value > 1 && maximumExponent[value] < 1 { maximumExponent[value] = 1 } } // すべてのb_iを割り切る共通分母を構築する。 commonDenominator := big.NewInt(1) primePowers := make([]PrimePower, 0) for prime := 2; prime <= MaxAB; prime++ { exponent := maximumExponent[prime] if exponent == 0 { continue } primePowers = append(primePowers, PrimePower{ prime: prime, exponent: exponent, }) primeValue := big.NewInt(int64(prime)) for count := 0; count < exponent; count++ { commonDenominator.Mul( commonDenominator, primeValue, ) } } graph := make([][]Edge, n) for _, raw := range rawEdges { scaledWeight := new(big.Int).Set(commonDenominator) scaledWeight.Quo( scaledWeight, big.NewInt(int64(raw.denominator)), ) scaledWeight.Mul( scaledWeight, big.NewInt(int64(raw.numerator)), ) // scaledWeightは以後変更しないため、 // 両方向の辺で共有できる。 graph[raw.u] = append(graph[raw.u], Edge{ to: raw.v, weight: scaledWeight, }) graph[raw.v] = append(graph[raw.v], Edge{ to: raw.u, weight: scaledWeight, }) } distance := make([]*big.Int, n) reached := make([]bool, n) queue := &PriorityQueue{} heap.Init(queue) reached[0] = true distance[0] = big.NewInt(0) heap.Push(queue, State{ distance: big.NewInt(0), vertex: 0, }) for queue.Len() > 0 { current := heap.Pop(queue).(State) if !reached[current.vertex] || current.distance.Cmp(distance[current.vertex]) != 0 { continue } for _, edge := range graph[current.vertex] { // 任意精度整数の加算を一度だけ行う。 nextDistance := new(big.Int).Add( current.distance, edge.weight, ) if !reached[edge.to] || nextDistance.Cmp(distance[edge.to]) < 0 { reached[edge.to] = true distance[edge.to] = nextDistance heap.Push(queue, State{ distance: nextDistance, vertex: edge.to, }) } } } var output bytes.Buffer output.Grow(8 * 1024 * 1024) for vertex := 1; vertex < n; vertex++ { numerator := new(big.Int).Set(distance[vertex]) denominator := new(big.Int).Set(commonDenominator) remainder := new(big.Int) // 分母の素因数分解は既知なので、 // 各素数で割れる回数だけ約分する。 for _, primePower := range primePowers { primeValue := big.NewInt(int64(primePower.prime)) for count := 0; count < primePower.exponent; count++ { remainder.Mod(numerator, primeValue) if remainder.Sign() != 0 { break } numerator.Quo(numerator, primeValue) denominator.Quo(denominator, primeValue) } } output.WriteString(numerator.String()) output.WriteByte(' ') output.WriteString(denominator.String()) output.WriteByte('\n') } if _, err := os.Stdout.Write(output.Bytes()); err != nil { panic(err) } }