1.原理
费马小定理:p为质数,p∤a时,a^(p-1)≡1(mod p)
两边同除a,则inv(a)=a^(p-2)%p
因此,只要用快速幂求出a的p-2次幂即可求出逆元
2.题目
线性求逆元(非严格)
时间限制:1秒 内存限制:128M
题目描述
给定n,p,求1到n所有整数在模p意义下的乘法逆元。
输入描述
两个正整数n,p(1≤n≤3×106,n<p<20000528),保证p是质数
输出描述
输出n行,第i行代表i在模p意义下的乘法逆元
解题思路
根据取模的性质,我们可以知道inv[a]*inv[b]=inv[a*b],所以我们可以通过ola筛算出质数逆元,在此基础上通过相乘求出合数逆元
AC代码
#include<bits/stdc++.h>
using namespace std;
long long tmp,ans[3000006];
long long n,p,pr[3000006],cnt,st[3000006],inv[3000006],mod;
long long qp(long long a ,long long n){
long long re=1;
while(n){
if(n&1){
re=(re*a)%mod;
}
n>>=1;
a=(a*a)%mod;
}
re%mod;
return re;
}
void ola(int n){
st[1]=1;
inv[1]=1;
for(int i=2;i<=n;i++){
if(!st[i]){
pr[cnt++]=i;
inv[i]=qp(i,mod-2);
}
for(int j=0;j<cnt&&i*pr[j]<=n;j++){
st[i*pr[j]]=1;
inv[i*pr[j]]=(inv[i]*inv[pr[j]])%mod;
if(i%pr[j]==0){
break;
}
}
}
}
int main(){
cin>>n>>p;
mod=p;
ola(n);
for(int i=1;i<=n;i++){
cout<<inv[i]<<'\n';
}
return 0;
}
T2
线性求逆元2
时间限制:1秒 内存限制:256M
题目描述
给出n个正整数ai,求:
输入描述
第一行一个正整数n(1≤n≤5×10e6)
第二行n个整数ai(1≤ai<p)
解题思路
通过变形,我们推出下列公式:ans=(((inva1k+inva2)k+inva3)k+..)k+invan
所以只要求出a[1]->a[n]的逆元即可
由于数据庞大,我们使用严格线性求逆元
求前缀积s[i]=s[i-1]*a[i]
求前缀积的逆元(此处求sinv[n])sinv[i-1]=sinv[i]*a[i]
原数组逆元inva[i]=sv[i]*s[i-1];
(注意初始化s[0],sinv!!!!!!!!!)
AC代码
#include<bits/stdc++.h>
using namespace std;
long long tmp,ans,n,p,mod=1000000007,a[5000005],s[5000005],sinv[5000005],inva[5000005],k=998244353;
long long qp(long long aa ,long long nn){
long long re=1;
while(nn){
if(nn&1){
re=(re*aa)%mod;
}
nn>>=1;
aa=(aa*aa)%mod;
}
re%mod;
return re;
}
int main(){
cin>>n;
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
s[0]=1;
s[1]=a[1];
for(int i=2;i<=n;i++){
s[i]=(s[i-1]*a[i])%mod;
}
sinv[n]=qp(s[n],mod-2);
for(int i=n;i>=1;i--){
sinv[i-1]=sinv[i]*a[i];
sinv[i-1]%=mod;
inva[i]=sinv[i]*s[i-1];
inva[i]%=mod;
}
tmp=0;
for(int i=1;i<=n;i++){
tmp=(tmp*k%mod+inva[i]);
tmp%=mod;
}
cout<<tmp;
return 0;
}