费马小定理
适用:求某一个数在模意义下的乘法逆元。
如果
a、p
互质,那么有
ap−a
就是p的倍数,所以有
ap≡a(modp)
,
ap−1≡1(modp)
。所以只要打一个快速幂就ok了。
code
#include<bits/stdc++.h>
using namespace std;
int n,p;
int power(int x,int k)
{
int ans=1;
while(k)
{
if(k&1)ans*=x%p;
ans%=p;
x=x*x%p;
k>>=1;
}
return ans%p;
}
int main()
{
scanf("%d%d",&n,&p);
printf("1\n");
for(int i=2;i<=n;i++)
printf("%d\n",power(i,p-2));
return 0;
}
线性求法
适用:某一区间的所有的数在模意义下的乘法逆元、求单个逆元(递归求解)。
简单地说就是一个递推。
首先我们有:
1−1≡1(modp)
然后,我们设
p=k×i+r,r<l,1<i<p
再将这个式子放到
modp
意义下一看:
k×i+r≡0(modp)
再两边同时乘上
i−1,r−1
可以得到:
k×r−1+i−1≡0(modp)
移项:
i−1=−k×r−1(modp)
所以:
i−1≡−[pi]×(pmodi)−1
于是乎我们就可以递推处当前的逆元了:
A[i]=-(p/i)*A[p%i]
然后其实吧,我们还可以通过递归求解,在
O(log2p)
的时间内求出单个逆元。怎么证明时间复杂度呢:由于我们可以发现
pmodi<i/2
,所以每次的子问题规模减半,最终递归次数也可见了。
code:
#include<bits/stdc++.h>
#define maxn 3000005
using namespace std;
int n;
long long p;
long long ans[maxn];
int main()
{
scanf("%d%lld",&n,&p);
ans[1]=1;
for(int i=2;i<=n;i++)
ans[i]=(-(p/i)*ans[p%i])%p;
for(int i=1;i<=n;i++)
printf("%lld\n",ans[i]>0 ? ans[i] : ans[i]+p);
return 0;
}