1. 取模的数学规则
1.1 基本四则运算
\[ \begin{cases} (a + b) \bmod p = (a \bmod p + b \bmod p) \bmod p\\ (a - b) \bmod p = (a \bmod p - b \bmod p + p) \bmod p\\ (a * b) \bmod p = (a \bmod p * b \bmod p) \bmod p\\ (a ^ b) \bmod p = ((a \bmod p) ^ b) \bmod p\\ (a \bmod p) \bmod p = a \bmod p \end{cases} \]
1.2 其他性质
- 交换律
\[ (a + b) \bmod c = (b + a) \bmod c\\ (a * b) \bmod c = (b * a) \bmod c \]
- 结合律
\[ ((a+b) \bmod p + c) \bmod p = (a + (b+c) \bmod p) \bmod p\\ ((a * b) \bmod p * c) \bmod p = (a * (b * c) \bmod p) \bmod p\\ \]
- 分配律
\[ ((a +b)\bmod p * c) \bmod p = ((a * c) \bmod p + (b * c) \bmod p) \bmod p \]
2. 重要应用
有一种常见的题型,要求对一个大整数求模运算, 这时我们就可以灵活运用求模运算的性质。
例如下面这题:
求斐波那契数列 \(F_n\) 除以10007的余数是多少(1 <= n <= 1000000)
这里很显然主要的问题就是溢出问题,一下这种写法很显然会溢出:
1 | long a = 1, b = 1, res = 2; |
利用取模的性质,我们可以改写为:
1 | for (int i = 3; i <= n; ++i) |
再举一个例子:
输入n,求前n个阶乘相加后的数的后6位(不包含前导0)。n <= 10^6
如果我们不利用性质,就会写出一个比较危险的代码:
1 | long factorial = 1; |
显然百分百会溢出。但是如果我们更改一下,重复利用数学性质,则有:
1 | long factorialx = 1; |
证明略。