基本定义 :设 m m m 是正整数,如果 a , b a,b a,b 的差 a − b a-b a−b 被 m m m 整除即 a − b = q m a-b=qm a−b=qm,就称 a , b a, \ b a, b 关于模 m m m 同余,或简称同余。记为
a ≡ b ( m o d m ) a\equiv b\pmod m a≡b(modm)
简单来说就是如果 a a a 和 b b b 除以 m m m 的余数相同,那么 a , b a, \ b a, b 就关于模 m m m 同余。
2. 同余的性质
若 a ≡ b ( m o d m ) a\equiv b\pmod m a≡b(modm), c ≡ d ( m o d m ) c\equiv d\pmod m c≡d(modm),则 a ± c ≡ b ± d ( m o d m ) a\pm c\equiv b\pm d\pmod m a±c≡b±d(modm);
若 a ≡ b ( m o d m ) a\equiv b\pmod m a≡b(modm), c ≡ d ( m o d m ) c\equiv d\pmod m c≡d(modm),则 a c ≡ b d ( m o d m ) ac\equiv bd\pmod m ac≡bd(modm);
若 a ≡ b ( m o d m ) a\equiv b\pmod m a≡b(modm), n ∈ N n\in N n∈N,则 a n ≡ b n ( m o d m ) a^n\equiv b^n\pmod m an≡bn(modm);
若 a c ≡ b c ( m o d m ) ac\equiv bc\pmod m ac≡bc(modm),且 c c c 与 m m m 互质 ,则 a ≡ b ( m o d m ) a\equiv b\pmod m a≡b(modm);
a ≡ b ( m o d m ) a\equiv b\pmod m a≡b(modm) ⇔ \Leftrightarrow ⇔ a − b = k m a-b=km a−b=km。
可见同余的两侧的数字同时加、减、乘一个数字没有限制,但是同时除以一个数时有一定的限制。
二、费马小定理
1. 费马小定理的内容
费马小定理 :如果 p p p 是一个质数,对于任意整数 a a a,都有
a p ≡ a ( m o d p ) a^p\equiv a\pmod p ap≡a(modp)
此外我们还会见到另一种形式:如果 p p p 是一个质数,a a a 不能被 p p p 整除 ,那么有
a p − 1 ≡ 1 ( m o d p ) a^{p-1}\equiv1\pmod p ap−1≡1(modp)
这是由于同余的性质,如果两边同时除以 a a a 的话,必须要满足 a a a 和 p p p 互质,由于 p p p 是质数,所以等同余 a a a 不能被 p p p 整除。
2. 费马小定理的证明(趣味版)
证明:定理的证明千篇一律,有趣的思维万里挑一。这里我想用一种有趣的方式进行证明,过程如下:
假设现在有一串珠子形成了一个环,环上珠子的数量是 p p p( p p p是素数),现在我有 a a a 种颜色,我要给这个环上的每一个珠子进行上色。根据小学知识,每个珠子的上色情况都有 a a a 种,因此总共有 a p a^p ap 种上色方式。
假如我要用至少两种颜色进行上色,总共就有 ( a p − a ) (a^p-a) (ap−a) 种上色方式。我们现在要证明的是 a p − a ≡ 0 ( m o d p ) a^p-a\equiv0\pmod p ap−a≡0(modp),也就是证明 a p − a a^p-a ap−a 这个式子能够被 p p p 整除,即证明 a p − a = k p a^p-a=kp ap−a=kp 。
又回到这个给环上色问题上,问题就可以转化为:如果我们可以把至少用两种颜色这一情况分成 k k k 组,每组都刚好有 p p p 种上色方式
那我们就可以证明 a p − a a^p-a ap−a 是 p p p 的 k k k 倍了!
总共 a p a^p ap 种上色方式 { 一种颜色 : a 至少两种颜色: a p − a { p 种 p 种 ⋮ p 种 \begin{cases}一种颜色:a\\\\\\至少两种颜色:a^p-a\begin{cases}p种\\p种\\\vdots \\p种\end{cases}\end{cases} ⎩ ⎨ ⎧一种颜色:a至少两种颜色:ap−a⎩ ⎨ ⎧p种p种⋮p种
为了方便,我们把可以通过旋转得到一模一样的两种上色方式称为"同款",如下:
现在我们把 "同款" 的上色方式放在一组里面,由上图不难发现,当我们关注红色的珠子时,当它绕着旋转一圈后,它把每个位置都经过了一边。也就是说,当我们把这个环进行旋转时,它所出现的不同上色方式恰好就是这个环上的珠子的个数,也就是 p p p。也就说明了同款里不同的上色方式就是 p p p 种 。即我们确实可以把 ( a p − a ) (a^p-a) (ap−a) 种上色方式分成若干个 p p p 种上色方式。
BUT!这好像有个bug啊...万一在旋转过程中出现了两个一模一样的上色咋办?请看VCR:
别急!这个时候, p p p 是素数的条件就很关键了。既然是素数,就说明它只能被1和它本身整除,所以说实际上是不可能出现像上面那样有几个完整周期排列的。只要不是由几个完整的周期依次排列成环的,就不会出现像上面那样转了不到一圈就完全重合的现象。
综上,我们就说明了在 ( a p − a ) (a^p-a) (ap−a) 种上色方式中,我们可以把它分成 k k k 组,每组都恰好有 p p p 种上色方式。即
a p − a = k p ⇕ a p − a ≡ 0 ( m o d p ) ⇕ a p ≡ a ( m o d p ) a^p-a=kp\\ \Updownarrow\\ a^p-a\equiv0\pmod p\\ \Updownarrow\\ a^p\equiv a\pmod p\\ ap−a=kp⇕ap−a≡0(modp)⇕ap≡a(modp)
费马小定理,证毕!
3. 求解乘法逆元
(1) 乘法逆元
定义
对于正整数 a a a 和 p p p,若有
a x ≡ 1 ( m o d p ) ax\equiv1\pmod p ax≡1(modp)
那么把这个同余方程中的 x x x 的解叫做 a a a 模 p p p 的乘法逆元,简称逆元 ,记作 a − 1 a^{-1} a−1。
例如 8 x ≡ 1 ( m o d 5 ) 8x \equiv 1 \pmod 5 8x≡1(mod5) 中, x = 2 , 7 , ⋯ x = 2, 7, \cdots x=2,7,⋯ 。那么解出来的这些 x x x 就是 8 8 8 模 5 5 5 的乘法逆元。
a a a 存在乘法逆元的充要条件是 a a a 和 p p p 互质
要想说明 a x ≡ 1 ( m o d p ) ax\equiv1\pmod p ax≡1(modp) 存在这么一个 x x x ,可以进行一个简单的变形。因为是对 a x ax ax 取模,相当于是把 a x ax ax 减掉了 k k k 个 p p p 最终剩下了 1 1 1,相当于可以写成 a x − k p = 1 ax-kp=1 ax−kp=1, k k k 是一个整数,这里等价于 a x + p y = 1 ax+py=1 ax+py=1。而根据裴蜀定理,方程 a x + p y = 1 ax+py=1 ax+py=1 有解的充要条件是 gcd ( a , p ) = 1 \gcd(a,p)=1 gcd(a,p)=1。
(2) 费马小定理 + 快速幂求逆元
a a a 存在乘法逆元的充要条件是 a a a 与 p p p 互质,而**当模数 p p p 是质数时,**满足费马小定理的条件,由费马小定理:
a p − 1 ≡ 1 ( m o d p ) a^{p-1}\equiv1\pmod p ap−1≡1(modp)
提出一个 a a a 出来,可以得到
a ⋅ a p − 2 ≡ 1 ( m o d p ) a\cdot a^{p-2}\equiv1\pmod p a⋅ap−2≡1(modp)
而这个式子就恰好满足了我们希望的形式,因此可以看出, a a a 的逆元就是 a p − 2 a^{p-2} ap−2,而 a p − 2 a^{p - 2} ap−2 则可用快速幂进行求解。
注:当 p p p 不是质数的时候,需要使用其他方法进行求解,例如扩展欧几里得算法。
cpp复制代码
#include <iostream>
using namespace std;
typedef long long LL;
// 必须要保证 a, p 互质,且 p 为质数
LL qpow(LL a, LL b, LL p)
{
LL ret = 1;
while(b)
{
if(b & 1) ret = ret * a % p;
b >>= 1;
a = a * a % p;
}
return ret;
}
int main()
{
LL x, p; cin >> n >> p;
cout << qpow(x, p - 2, p) << endl; // 打印 x 在模 p 意义下的乘法逆元
return 0;
}
时间复杂度与快速幂一致,为 O ( log n ) O(\operatorname{log}n) O(logn)。
我们把法力值消耗为 1 1 1 的【一串香蕉(还剩 n n n 根)】记为 ( 1 , n ) (1, n) (1,n),以及场上有 k k k 个【水上舞者索尼娅】。此时我们使用一次一串香蕉,根据题意,你会得到 k k k 个 ( 0 , n ) (0, n) (0,n) 和 一个 ( 1 , n − 1 ) (1, n - 1) (1,n−1),此时再把 k k k 个 ( 0 , n ) (0, n) (0,n) 全部使用,那么就会得到 k k k 个 ( 1 , n − 1 ) (1, n - 1) (1,n−1),这个时候,你会得到 k + 1 k + 1 k+1 个 ( 1 , n − 1 ) (1, n - 1) (1,n−1)。
复制代码
(1, n) --使用k+1次--> (k + 1)个(1, n - 1)
那么以此类推,我们可以得出以下规律
复制代码
(1, n) --使用k+1次--> (k + 1)个(1, n - 1) --使用(k+1)^2次--> (k + 1)^2个(1, n - 2) ...
所以我们最终就可以使用 ( k + 1 ) + ( k + 1 ) 2 + ⋯ + ( k + 1 ) n = ∑ i = 1 n ( k + 1 ) i (k + 1) + (k + 1)^2 + \cdots + (k + 1)^n = \sum\limits_{i = 1}^{n}(k + 1)^i (k+1)+(k+1)2+⋯+(k+1)n=i=1∑n(k+1)i 张【一根香蕉】。
根据等比数列求和公式再进行整理,最终答案就是 ( k + 1 ) n + 1 − k − 1 k m o d ( 1 0 9 + 7 ) \frac{(k+1)^{n + 1} - k - 1}{k} \bmod (10^9 + 7) k(k+1)n+1−k−1mod(109+7)。由于在模运算中出现了除法,所以等价于求解 [ ( k + 1 ) n + 1 − k − 1 ] × k − 1 m o d ( 1 0 9 + 7 ) [(k+1)^{n + 1} - k - 1]\times k^{-1} \bmod (10^9 + 7) [(k+1)n+1−k−1]×k−1mod(109+7)。
(2) 代码实现
cpp复制代码
#include<iostream>
using namespace std;
typedef long long LL;
const int p = 1e9 + 7;
LL n, k;
// 快速幂
LL qpow(LL a, LL n, LL p)
{
LL ret = 1;
while(n)
{
if(n & 1) ret = ret * a % p;
n >>= 1;
a = a * a % p;
}
return ret;
}
// 求解 x 模 p 下的逆元
LL inv(LL x, LL p)
{
return qpow(x, p - 2, p);
}
int main()
{
int t;
cin >> t;
while(t--)
{
cin >> n >> k;
LL ans = (qpow(k + 1, n + 1, p) - k - 1) * inv(k, p);
// 注意把答案补成最小非负整数
cout << ((ans % p) + p) % p << endl;
}
}