ABC110-D Factorization
概要
が与えられ, を満たすの組み合わせを求める.
考察
はの積で与えられるので,素因数分解して因数を個に振り分ける方法を考える.
が種類の因数からなるとすると,は素数及びべき数を用いて以下のように表される.
よってこれはのべき数を個に振り分ける重複組み合わせ問題になる.
素因数を重複を許して個に振り分ける組み合わせは で求められる. 各素因数の振り分けは独立な事象であるから,各素因数の組み合わせの積(とMOD)をとればよい.
(本来は愚直にやっても通るっぽいけど),モジュラ逆数を用いて除算を助けてもらうことにする.
Modular multiplicative inverse
この問題では愚直にnCrを求めても間に合うが,そもそも間に合わない問題もあるらしい.
modular multiplicative inverseを利用すると,前処理 クエリ でmod pにおける逆元を計算できる. 逆元を用いると,mod計算など有限体上での除算が行えるようになる.
(逆元使わないでこの問題解けるっぽいけど割り算とMODの順番がどうも合わなくてWA)
逆元の求め方は1より.
struct Modulo
で逆元の体を定義して,new()
時に 及び を計算し,comb
でnCrを計算する.
pub struct Modulo {
fact: Vec<usize>,
inv_fact: Vec<usize>,
modulo: usize
}
impl Modulo {
pub fn new(n: usize, modulo: usize) -> Self {
let mut fact = vec![0; n + 1];
let mut inv = vec![0; n + 1];
let mut inv_fact = vec![0; n + 1];
inv[1] = 1;
for i in 2..n + 1 {
inv[i] = inv[modulo % i] * (modulo - modulo / i) % modulo;
}
fact[0] = 1;
inv_fact[0] = 1;
for i in 0..n {
fact[i + 1] = fact[i] * (i + 1) % modulo;
}
for i in 0..n {
inv_fact[i + 1] = inv_fact[i] * inv[i + 1] % modulo;
}
Modulo { fact: fact, inv_fact: inv_fact, modulo: modulo }
}
pub fn comb(&self, n: usize, r: usize) -> usize {
self.fact[n] * self.inv_fact[r] %
self.modulo * self.inv_fact[n - r] % self.modulo
}
}
Comments