結果

問題 No.3669 误差绝不允许
コンテスト
ユーザー harurun
提出日時 2026-08-07 04:23:26
言語 Rust
(1.97.1 + proconio + num + itertools)
コンパイル:
/usr/bin/rustc_custom
実行:
./target/release/main
結果
AC  
実行時間 330 ms / 3,000 ms
+ 643µs
コード長 5,957 bytes
記録
記録タグの例:
初AC ショートコード 純ショートコード 純主流ショートコード 最速実行時間
コンパイル時間 11,586 ms
コンパイル使用メモリ 196,868 KB
実行使用メモリ 28,084 KB
最終ジャッジ日時 2026-09-04 22:11:06
合計ジャッジ時間 18,615 ms
ジャッジサーバーID
(参考情報)
judge1_0 / judge2_0
このコードへのチャレンジ
(要ログイン)
ファイルパターン 結果
sample AC * 2
other AC * 30
権限があれば一括ダウンロードができます

ソースコード

diff #
raw source code

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<Ordering> {
        Some(self.cmp(other))
    }
}

struct Scanner {
    input: Vec<u8>,
    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<T: FromStr>(&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<Vec<Edge>> =
        (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 = &current.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();
}
0