费马小定理
费马是个很出名的大佬,比较熟悉就是费马大定理。今天我们来谈谈费马的小定理。这是数论里面一个非常重要的定理,在高中的数学联赛或者是信息学竞赛,以及大学的ACM竞赛都是很重要的一个定理。那么今天就来科普一下吧。
定理讲诉
费马小定理是这样的,对于整数
a
a
a,和质数
p
p
p,如果
a
a
a与
p
p
p互质,那么有
a
p
−
1
≡
1
(
m
o
d
p
)
a^{p-1} \equiv1(mod p)
ap−1≡1(modp),欧拉将其上升为
a
p
≡
a
(
m
o
d
p
)
a^p \equiv a(modp)
ap≡a(modp).
关于定理的证明,笔者这边写个我高中时会的一种方法(其实是到现在我也就只会这种方法qaq) .
证明
对于质数
p
p
p, 我们有序列
1
,
2
,
3
,
⋯
p
−
1
1,2,3,\cdots p-1
1,2,3,⋯p−1, 显然序列中的任何数均与
p
p
p互质,又
a
a
a与
p
p
p互质,那么可以得出对于任意
i
,
j
(
i
≠
j
)
,
a
⋅
i
≡
a
⋅
j
(
m
o
d
p
)
i,j(i\neq j) ,a\cdot i \equiv a\cdot j(mod p)
i,j(i̸=j),a⋅i≡a⋅j(modp)不成立,这个可以用反证法证明,即有
a
⋅
(
i
−
j
)
≡
0
(
m
o
d
p
)
a \cdot(i-j) \equiv 0(modp)
a⋅(i−j)≡0(modp)这与
a
a
a与
p
p
p互质时矛盾的,根据这点我们可以构造
(
a
⋅
1
)
(
a
⋅
2
)
⋯
(
a
⋅
(
p
−
1
)
)
≡
1
⋅
2
⋅
p
−
1
(
m
o
d
p
)
(a \cdot 1)(a \cdot 2) \cdots (a\cdot (p-1)) \equiv 1 \cdot 2 \cdot p-1(modp)
(a⋅1)(a⋅2)⋯(a⋅(p−1))≡1⋅2⋅p−1(modp)
=
>
a
p
−
1
⋅
(
p
−
1
)
!
≡
(
p
−
1
)
!
(
m
o
d
p
)
=>a^{p-1}\cdot(p-1)!\equiv(p-1)!(modp)
=>ap−1⋅(p−1)!≡(p−1)!(modp)
=
>
a
p
−
1
≡
1
(
m
o
d
p
)
=>a^{p-1}\equiv1(modp)
=>ap−1≡1(modp)
知道证明之后我们就来看看怎么实战吧,做题当中通常我们还得知道膜逆元这东西,膜,题目通常会让我们求
a
b
(
m
o
d
p
)
\frac{a}{b}(modp)
ba(modp)的值,这个时候就需要用到费马小定理,怎么用呢?考虑:
a
⋅
b
−
1
≡
x
(
m
o
d
p
)
a\cdot b^{-1} \equiv x(modp)
a⋅b−1≡x(modp)式中的
x
x
x就是我们要求的数,那么怎么求这个
x
x
x呢,考虑费马小定理,将两边同时乘上
b
p
−
1
b^{p-1}
bp−1,即
a
⋅
b
p
−
2
≡
x
⋅
b
p
−
1
(
m
o
d
p
)
a\cdot b^{p-2}\equiv x\cdot b^{p-1}(mod p)
a⋅bp−2≡x⋅bp−1(modp)又根据费马小定理有
a
p
−
1
≡
1
(
m
o
d
p
)
a^{p-1}\equiv1(modp)
ap−1≡1(modp),可以得出
a
⋅
b
p
−
2
≡
x
(
m
o
d
p
)
a\cdot b^{p-2} \equiv x(modp)
a⋅bp−2≡x(modp)
就得出答案了。
说了这么多,看到题吧。
https://nanti.jisuanke.com/t/A2017
这道题比较容易分析出答案为 2 n − 1 m o d p 2^{n-1}modp 2n−1modp,这个证明直接递推下就有了。但让人蛋疼的是这个 n n n高达 1 0 100000 10^{100000} 10100000这么大。直接求肯定是不行的,那么这个时候就可以用费马小定理了,仔细分析你可以发现,答案其实就是 2 ( n ) m o d ( p − 1 ) m o d p ⋅ 2 p − 2 2^{(n)mod(p-1)}modp\cdot2^{p-2} 2(n)mod(p−1)modp⋅2p−2, 那么就很明了,加上一个快速幂和同余就解决了
AC代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+5;
const ll mod=1e9+7;
string str;
ll poww(ll a,ll b){
ll ans=1,base=a;
while(b){
if(b&1){
ans*=base;
ans%=mod;
}
b>>=1;
base*=base;
base%=mod;
}
return ans;
}
int main(){
int t;
scanf("%d",&t);
while(t--){
cin>>str;
int n=str.length();
ll ans=1,a=0;
for(int i=0;i<n;i++){
a=a*10+(str[i]-'0');
a%=(mod-1);
}
ans=poww(2,a);
ans=(ans*poww(2,mod-2))%mod;
printf("%lld\n",ans);
}
return 0;
}
新的开始,每天都要快乐哈!