题目
给定 n , p n,p n,p 求 1 ∼ n 1\sim n 1∼n 中所有整数在模 p p p 意义下的乘法逆元。
思路
定义
首先看逆元的定义:
若
a
∗
x
≡
1
(
m
o
d
b
)
a*x\equiv1 \pmod {b}
a∗x≡1(modb),且
a
a
a 与
b
b
b 互质,
那么我们就能定义:
x
x
x 为
a
a
a 的逆元,记为
a
−
1
a^{-1}
a−1,
所以我们也可以称
x
x
x 为
a
a
a 在
m
o
d
b
\bmod b
modb 意义下的倒数。
逆元有很多应用,比如在模意义下的乘法中。
对于
a
b
(
m
o
d
p
)
\displaystyle\frac{a}{b} \pmod {p}
ba(modp) ,我们就可以求出
b
b
b 在
m
o
d
p
\bmod {p}
modp 下的逆元,然后乘上
a
a
a ,再
m
o
d
p
\bmod {p}
modp,就是这个分数的值了。
那怎么求逆元呢?
求逆元呢,有三种做法,我会一一解释。
求法
1.exgcd求逆元
……
exgcd求逆元代码
//已卡常,已开O2
#include<iostream>
#include<cstdio>
using namespace std;
int n,p,x,y;
inline void exgcd(int a,int b,int &x,int &y)
{
if(b==0)
{
x=1;
y=0;
return;
}
exgcd(b,a%b,x,y);
int t=x;
x=y;
y=t-(a/b)*y;
}
inline void print(int x)
{
if(x<0)
{
putchar('-');
x=-x;
}
if(x>9)
print(x/10);
putchar(x%10+'0');
}
int main()
{
scanf("%d%d",&n,&p);
for(register int i=1; i<=n; ++i)
{
exgcd(i,p,x,y);
x=(x%p+p)%p;
print(x);
puts("");
}
return 0;
}
2.费马小定理求逆元
……
3.线性求逆元
线性求逆元呢,其实不需要用到什么算法或定理,直接推式子就可以了。
本题只有线性求逆元能AC,其余两种卡常也只能跑到550ms。(时限500ms)。
线性求逆元代码
#include<iostream>
#include<cstdio>
using namespace std;
long long n,p,inv[30000010];
int main()
{
scanf("%lld%lld",&n,&p);
printf("1\n");
inv[1]=1;
for(long long i=2; i<=n; i++)
{
inv[i]=p-(p/i)*inv[p%i]%p;
printf("%lld\n",inv[i]);
}
return 0;
}