haar_lib/math/fps/
sqrt_sparse.rs1use crate::math::mod_ops::sqrt::mod_sqrt;
3use crate::math::polynomial::sparse::SparsePolynomial;
4use crate::math::polynomial::Polynomial;
5use crate::math::prime_mod::PrimeMod;
6use crate::num::const_modint::*;
7
8pub trait FpsSqrtSparse {
10 type Output;
12
13 fn fps_sqrt_sparse(self, n: usize) -> Result<Self::Output, &'static str>;
15}
16
17impl<P: PrimeMod> FpsSqrtSparse for SparsePolynomial<P> {
18 type Output = Polynomial<P>;
19
20 fn fps_sqrt_sparse(self, n: usize) -> Result<Self::Output, &'static str> {
22 let Some(k) = (0..n).find(|&i| self.coeff_of(i).value() != 0) else {
23 return Ok(vec![ConstModInt::new(0); n].into());
24 };
25
26 if k % 2 == 1 {
27 return Err("最小次数が偶数ではない。");
28 }
29
30 let a = self.coeff_of(k);
31 let sr = ConstModInt::new(
32 mod_sqrt(a.value() as u64, P::PRIME_NUM as u64)
33 .ok_or("最小次数項の係数に平方根が存在しない。")? as u32,
34 );
35
36 let mut f = self;
37 f.shift_lower(k);
38 f.scale(a.inv());
39
40 let mut g = vec![ConstModInt::new(0); n];
41 let mut ret = vec![ConstModInt::new(0); n];
42 ret[0] = 1.into();
43
44 let mut invs = vec![ConstModInt::new(1); n + 1];
45 for i in 2..=n {
46 invs[i] = -invs[P::PRIME_NUM as usize % i] * ConstModInt::new(P::PRIME_NUM / i as u32);
47 }
48
49 let half = ConstModInt::new(2).inv();
50
51 for i in 0..n - 1 {
52 let mut s = ConstModInt::new(0);
53
54 for (&j, &fj) in f.iter() {
55 if j != 0 {
56 if i >= j {
57 s -= fj * g[i - j];
58 }
59 if i + 1 >= j {
60 s += ret[i - j + 1] * j.into() * fj * half;
61 }
62 }
63 }
64
65 g[i] = s;
66 ret[i + 1] = s * invs[i + 1];
67 }
68
69 let mut ret = Polynomial::from(ret);
70 ret.scale(sr);
71 ret.shift_higher(k / 2);
72 Ok(ret)
73 }
74}