atcoder/lib/math/
combinatorics.rs1use super::mod_int::ModInt;
4
5#[derive(Debug, Clone)]
33pub struct Combinatorics<const MOD: i64> {
34 factorial: Vec<ModInt<MOD>>,
35 inverse_factorial: Vec<ModInt<MOD>>,
36}
37
38impl<const MOD: i64> Combinatorics<MOD> {
39 pub fn new(max_n: usize) -> Self {
49 assert!(MOD > 1, "modulus must be greater than one");
50
51 let max_n_i64 = i64::try_from(max_n).expect("max_n does not fit in i64");
52
53 assert!(max_n_i64 < MOD, "max_n must be smaller than modulus");
54
55 let mut factorial = vec![ModInt::new(1); max_n + 1];
56
57 for i in 1..=max_n {
58 factorial[i] = factorial[i - 1] * ModInt::new(i as i64);
59 }
60
61 let mut inverse_factorial = vec![ModInt::new(1); max_n + 1];
64
65 inverse_factorial[max_n] = factorial[max_n].inv();
66
67 for i in (1..=max_n).rev() {
68 inverse_factorial[i - 1] = inverse_factorial[i] * ModInt::new(i as i64);
69 }
70
71 Self {
72 factorial,
73 inverse_factorial,
74 }
75 }
76
77 pub fn max_n(&self) -> usize {
79 self.factorial.len() - 1
80 }
81
82 pub fn factorial(&self, n: usize) -> ModInt<MOD> {
90 self.assert_in_range(n);
91 self.factorial[n]
92 }
93
94 pub fn permutation(&self, n: usize, r: usize) -> ModInt<MOD> {
99 self.assert_in_range(n);
100
101 if r > n {
102 return ModInt::new(0);
103 }
104
105 self.factorial[n] * self.inverse_factorial[n - r]
106 }
107
108 pub fn combination(&self, n: usize, r: usize) -> ModInt<MOD> {
113 self.assert_in_range(n);
114
115 if r > n {
116 return ModInt::new(0);
117 }
118
119 self.factorial[n] * self.inverse_factorial[r] * self.inverse_factorial[n - r]
120 }
121
122 fn assert_in_range(&self, n: usize) {
123 assert!(
124 n <= self.max_n(),
125 "n exceeds the precomputed range: n = {n}, max_n = {}",
126 self.max_n(),
127 );
128 }
129}
130
131#[cfg(test)]
132mod tests {
133 use super::Combinatorics;
134
135 type Comb = Combinatorics<998_244_353>;
136
137 #[test]
138 fn calculates_factorials_and_combinations() {
139 let comb = Comb::new(10);
140
141 assert_eq!(comb.factorial(0).value(), 1);
142 assert_eq!(comb.factorial(5).value(), 120);
143
144 assert_eq!(comb.permutation(5, 0).value(), 1);
145 assert_eq!(comb.permutation(5, 2).value(), 20);
146 assert_eq!(comb.permutation(5, 6).value(), 0);
147
148 assert_eq!(comb.combination(5, 0).value(), 1);
149 assert_eq!(comb.combination(5, 2).value(), 10);
150 assert_eq!(comb.combination(5, 5).value(), 1);
151 assert_eq!(comb.combination(5, 6).value(), 0);
152 }
153
154 #[test]
155 fn satisfies_pascals_identity() {
156 let comb = Comb::new(30);
157
158 for n in 1..=30 {
159 for r in 1..n {
160 let lhs = comb.combination(n, r).value();
161
162 let rhs = (comb.combination(n - 1, r - 1).value()
163 + comb.combination(n - 1, r).value())
164 % 998_244_353;
165
166 assert_eq!(lhs, rhs, "n = {n}, r = {r}");
167 }
168 }
169 }
170}