- 对于正整数a和m,如果有ax≡1(modm),那么把这个同余方程中的x最小正整数解叫做a模x的逆元。
逆元一般用扩展欧几里得算法来求得,如果m为素数,那么还可以根据费马小定理得到逆元为。
推导过程:
详细见:欧拉定理与费马小定理
要了解欧拉定理,就要先了解欧拉函数:
首先什么是欧拉函数呢?
欧拉函数phi(n)就表示1-n中与n互质的数的个数
设Xi为1-n中与n互质的数(一共有phi(n)个)
那么我们可以知道phi(2) = 1 , phi(3) = 2…,我们也可以通过如下的递推式获得更大的欧拉函数的值
对于任意一个能被n整除的质数,有m = n/p
当m%p == 0 的时候,phi(n) = phi(m)*p
当m%p != 0的时候,phi(n) = phi(m)*(p-1)
下面给出一种证明方法:
-------------------------------------------欧拉函数的推导式的推导和证明-----------------------------------------------------
1)由质因数分解定理得,在n中至少能够分解出一个素数p,导致 n = pm,那么可以将1~n划分为一个区间的集合{ [m(i-1) , mi ] , 1<=i<=p },则对于每个区间[m(i-1)+1,mi],其中的每个数可以看做m(i-1)+j (1<=j<=m)。
2)所以gcd ( m*(i-1)+j , m )
= gcd ( m , (m*(i-1)+j)%m ) (根据秦九韶辗转相除法)
=gcd ( m , j )
那么也就是每个区间对应位置的数均与1m中的某个数gcd相同,互质便是gcd(a,b)==1,所以1n中与m互质的数有p*phi(m)个。
3)那么我们接下来进行分类讨论:
若m%p == 0 , 那么 m = ap(a为一个正整数),所以若gcd(b,m) == 1 ,那么 gcd ( b , p ) == 1 , 所以 gcd ( mp , b ) == 1 , 即gcd ( n , b ) == 1 ,所以在这种情况与m互质的数均与n互质,所以phi(n) = phi(m)*p;
若m%p != 0 , 那么若gcd ( b , m) == 1 , gcd ( b , p ) == p , 那么 gcd ( n , b ) == p,所以1~m与m互质的数为q,若m%p != 0 , 那么与m互质且与p不互质的数是q*p,因此在n当中这样的数有phi(m)个,所以 phi(n) = phi(m)*p - phi(m)
-------------------------------------欧拉定理的内容--------------------------------------------------------------------------
a和n互质,那么a^phi(n) == 1 ( mod n )
------------------------------------欧拉定理的证明---------------------------------------------------------------------------
设xi为n当中与n互质的数,一共有phi(n)个:
1)设mi = a * xi , 则mi与n互质
因为a与n互质,mi也与n互质,所以说这个两个因数当中都没有与n相同的质因子,所以mi也不可能有,所以mi与n必然互质
2)因为mi与n互质,故mi不能被n整除
mi = pn+qr
若余数r与n不互质,那么d = gcd ( r , n )!=1 , mi = d*c(c代表一个正整数),那么gcd ( mi , n ) != 1 , 与mi和n互质矛盾!
故mi%n == ri, ri 均与n互质
3)若mi == mj ( mod n ) (i!=j)
mi-mj==a*(xi-xj)(mod n ) == 0 ( mod n )
xi和xj均是小于n的正整数,故xi==xj时才能成立,则与i!=j矛盾
故mi%n的余数均不相同,因为mi的个数与n以内与n以内与n互质的数的个数相同,所以ri就是xi的一个不同的排列。
所以从1n的mi的乘积与1n的xi的乘积模n下同余,所以约掉相同的部分,aaaa…(phi(n)个)==1(mod n ) , 那么原式得证
---------------------------------费马小定理的证明---------------------------------------------------------------------------------
费马小定理其实就是欧拉定理在n是素数时的特例:
a^phi(n) == 1 ( mod n )
当n是素数时,phi(n) = n-1
那么a^(p-1)== 1 ( mod p )
现在来看一个逆元最常见问题,求如下表达式的值:已知a,b
这个经典的问题有很多方法,最常见的就是扩展欧几里得,如果m是素数,还可以用费马小定理。
但是你会发现费马小定理和扩展欧几里得算法求逆元是有局限性的,它们都会要求与互素。实际上我们还有一种通用的求逆元方法,适合所有情况。公式如下:
证明步骤如下:
关于逆元的题目
参考代码:
#include <iostream>
#include <string.h>
#include <stdio.h>
using namespace std;
typedef long long LL;
const int N = 10005;
const int MOD = 9901;
bool prime[N];
int p[N];
int cnt;
void isprime()
{
cnt = 0;
memset(prime,true,sizeof(prime));
for(int i=2; i<N; i++)
{
if(prime[i])
{
p[cnt++] = i;
for(int j=i+i; j<N; j+=i)
prime[j] = false;
}
}
}
LL power(LL a,LL b)
{
LL ans = 1;
a %= MOD;
while(b)
{
if(b & 1)
{
ans = ans * a % MOD;
b--;
}
b >>= 1;
a = a * a % MOD;
}
return ans;
}
LL sum(LL a,LL n)
{
if(n == 0) return 1;
LL t = sum(a,(n-1)/2);
if(n & 1)
{
LL cur = power(a,(n+1)/2);
t = (t + t % MOD * cur % MOD) % MOD;
}
else
{
LL cur = power(a,(n+1)/2);
t = (t + t % MOD * cur % MOD) % MOD;
t = (t + power(a,n)) % MOD;
}
return t;
}
void Solve(LL A,LL B)
{
LL ans = 1;
for(int i=0; p[i]*p[i] <= A; i++)
{
if(A % p[i] == 0)
{
int num = 0;
while(A % p[i] == 0)
{
num++;
A /= p[i];
}
ans *= sum(p[i],num*B) % MOD;
ans %= MOD;
}
}
if(A > 1)
{
ans *= sum(A,B) % MOD;
ans %= MOD;
}
cout<<ans<<endl;
}
int main()
{
LL A,B;
isprime();
while(cin>>A>>B)
Solve(A,B);
return 0;
}
参考代码:
#include <iostream>
#include <string.h>
#include <stdio.h>
using namespace std;
typedef long long LL;
const int N = 10005;
const int MOD = 9901;
bool prime[N];
int p[N];
int cnt;
void isprime()
{
cnt = 0;
memset(prime,true,sizeof(prime));
for(int i=2; i<N; i++)
{
if(prime[i])
{
p[cnt++] = i;
for(int j=i+i; j<N; j+=i)
prime[j] = false;
}
}
}
LL multi(LL a,LL b,LL m)
{
LL ans = 0;
a %= m;
while(b)
{
if(b & 1)
{
ans = (ans + a) % m;
b--;
}
b >>= 1;
a = (a + a) % m;
}
return ans;
}
LL quick_mod(LL a,LL b,LL m)
{
LL ans = 1;
a %= m;
while(b)
{
if(b & 1)
{
ans = multi(ans,a,m);
b--;
}
b >>= 1;
a = multi(a,a,m);
}
return ans;
}
void Solve(LL A,LL B)
{
LL ans = 1;
for(int i=0; p[i]*p[i] <= A; i++)
{
if(A % p[i] == 0)
{
int num = 0;
while(A % p[i] == 0)
{
num++;
A /= p[i];
}
LL M = (p[i] - 1) * MOD;
ans *= (quick_mod(p[i],num*B+1,M) + M - 1) / (p[i] - 1);
ans %= MOD;
}
}
if(A > 1)
{
LL M = MOD * (A - 1);
ans *= (quick_mod(A,B+1,M) + M - 1) / (A - 1);
ans %= MOD;
}
cout<<ans<<endl;
}
int main()
{
LL A,B;
isprime();
while(cin>>A>>B)
Solve(A,B);
return 0;
}
代码:
#include <iostream>
#include <string.h>
#include <stdio.h>
#include <bitset>
using namespace std;
typedef long long LL;
const int N = 10000005;
bitset<N> prime;
void isprime()
{
prime.set();
for(int i=2; i<N; i++)
{
if(prime[i])
{
for(int j=i+i; j<N; j+=i)
prime[j] = false;
}
}
}
LL ans1[N],ans2[N];
LL inv[N];
int main()
{
isprime();
int MOD,m,n,T;
scanf("%d%d",&T,&MOD);
ans1[0] = 1;
for(int i=1; i<N; i++)
ans1[i] = ans1[i-1] * i % MOD;
inv[1] = 1;
for(int i=2;i<N;i++)
{
if(i >= MOD) break;
inv[i] = (MOD - MOD / i) * inv[MOD % i] % MOD;
}
ans2[1] = 1;
for(int i=2; i<N; i++)
{
if(prime[i])
{
ans2[i] = ans2[i-1] * (i - 1) % MOD;
ans2[i] = ans2[i] * inv[i % MOD] % MOD;
}
else
{
ans2[i] = ans2[i-1];
}
}
while(T--)
{
scanf("%d%d",&n,&m);
LL ans = ans1[n] * ans2[m] % MOD;
printf("%lld\n",ans);
}
return 0;
}