AtCoder Beginner Contest 411 E

https://atcoder.jp/contests/abc411/tasks/abc411_e

入力例にあるようにどの目とどの目が出たら、というのではなく、この目が最大になる確率を計算します。全ての目を降順にソートして、ついでにそのサイコロで何番目に小さいかをペアにしておきます。最初はp = 1とし、そのサイコロで何番目かをiとすると、p/6がその確率で、p = p * (i - 1) / iとupdateします。

// E [max]
#![allow(non_snake_case)]


//////////////////// library ////////////////////

fn read<T: std::str::FromStr>() -> T {
    let mut line = String::new();
    std::io::stdin().read_line(&mut line).ok();
    line.trim().parse().ok().unwrap()
}

fn read_vec<T: std::str::FromStr>() -> Vec<T> {
    read::<String>().split_whitespace()
            .map(|e| e.parse().ok().unwrap()).collect()
}

// ax = by + 1 (a, b > 0)
fn linear_diophantine(a: i64, b: i64) -> Option<(i64, i64)> {
    if a == 1 {
        return Some((1, 0))
    }
    
    let q = b / a;
    let r = b % a;
    if r == 0 {
        return None
    }
    let (x1, y1) = linear_diophantine(r, a)?;
    Some((-q * x1 - y1, -x1))
}

fn inverse<const D: i64>(a: i64) -> i64 {
    let (x, _y) = linear_diophantine(a, D).unwrap();
    if x >= 0 {
        x % D
    }
    else {
        x % D + D
    }
}


//////////////////// process ////////////////////

const D: i64 = 998244353;

fn read_input() -> Vec<Vec<i64>> {
    let N: usize = read();
    let A: Vec<Vec<i64>> = (0..N).map(|_| read_vec()).collect();
    A
}

fn F(mut A: Vec<Vec<i64>>) -> i64 {
    for v in A.iter_mut() {
        v.sort()
    }
    let mut v: Vec<(i64, i64)> = A.into_iter().
                                    flat_map(|w| w.into_iter().zip(1..)).
                                    collect();
    v.sort_by(|a, b| b.cmp(a));
    
    let mut E: i64 = 0;
    let mut p: i64 = 1;
    for &(e, i) in v.iter() {
        E = (E + e * p % D * inverse::<D>(i)) % D;
        p = p * ((i - 1) as i64) % D * inverse::<D>(i) % D;
    }
    E
}

fn main() {
    let A = read_input();
    println!("{}", F(A))
}