【数论】分数取模 - 逆元
在 OI 中我们常常需要对答案取模。
对于输出为分数的题目,我们一般不愿意直接输出 double 造成浮点误差,输出 a/b 形式又过于麻烦,并且不支持取模。
所以对于分数,我们需要定义一种新的取模方法。
定义
我们从整数取模的定义对分数取模进行拓展,我们希望 \(\frac{a}{b} \bmod m\) 答案是一个整数。
整数取模中,\(a \equiv b \pmod m\) 的定义是 \(m~|~a-b\),同时对于任意整数 \(c\) 有 \(ca \equiv cb \pmod m\)。
于是同理对于分数取模 \(\frac{a}{b}\equiv c\pmod m\),我们应当尽量让其满足上面的公式,\(\frac{a}{b} b\equiv cb\pmod m\),也就是 \(a\equiv cb\pmod m\)
\(cb\bmod m\) 在 \(c\in [0, m)\) 时会遍历 \((b,m)\) 的倍数,也就是说我们需要满足 \(a | (b, m)\)。
这时让等式两边和模数同除 \((b, m)\),转化为 \((b, m) = 1\) 的情况。
也就是说,分数取模的问题可以强化为,对于 \((a, m) = 1\),找出一个 \(b\) 满足
我们就可以说 \(b\) 为 \(a\) 的逆元,也就是
显然该 \(b\) 在 \(\bmod m\) 条件下唯一。
计算
费马小定理
根据费马小定理,我们有一个十分简单的公式。
所以有
当 \(m\) 为质数 \(p\) 时,因为 \(\varphi(p) = p-1\),特别的,有
所以我们可以用快速幂 \(O(\log \varphi(m))\) 计算一个数的逆元。但对于非素数 \(m\),我们需要另一个方法计算 \(\varphi(m)\),或者使用扩展欧几里得。
扩展欧几里得
注意 \(ab \equiv 1 \pmod m\) 可以转化为,存在 \(k\) 使得
这显然是欧几里得算法的形式。
我们可以通过扩展欧几里得解得 \(a\) 的值。
复杂的为欧几里得的复杂度 \(O(\log m)\)。
多组逆元预处理
这是一个快速计算 \(n\) 个数逆元的技巧,这个技巧十分简单,但许多初学者并没有意识到。
对于计算一个序列 \(a_1, a_2, \dots, a_n\) 内所有数模 \(m\) 意义下的逆元,我们可以用 \(o(n + \log m)\) 的时间复杂的完成。
具体的,我们对序列 \(a\) 进行前缀乘法,\(s_i = \prod_{j=1}^i a_i\)。
然后处理出 \(s_n\) 的逆元。也就是 \(f_n = \frac{1}{\prod_{j=1}^n a_i} = \prod_{j=1}^n \frac{1}{a_i}\)
这时候我们可以反过来从 \(n\) 到 \(1\) 依次乘上 \(a_i\),计算得到 \(f_i = \prod_{j=1}^i \frac{1}{a_i}\)
最后前后相消,\(a_i\) 的逆元 \(a_i^{-1} = (\prod_{j=1}^i \frac{1}{a_i})(\prod_{j=1}^{i - 1} a_i) = f_is_{i-1}\)
浙公网安备 33010602011771号