P3811 【模板】乘法逆元
题意
求1-n所有整数在模p意义下的逆元。
分析
逆元
如果x满足(ax=1(\%p))(其中a p是给定的数)那么称(x)是在(%p)意义下(a)的逆元
A 拓展欧几里得算法
[ax=1(\%p)
]
转换一下也就是
[ax+py=1
]
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int extgcd(int a,int b,int&x,int&y){
if(b==0){
x=1;
y=0;
return a;
}
int g=extgcd(b,a%b,y,x);
y-=a/b*x;
return g;
}
int main(){
int a,b,x,y;
cin>>a>>b;
for(int i=1;i<=a;i++){
extgcd(i,b,x,y);
cout<<(x%b+b)%b<<endl;
}
}
得分:48。TLE
B 费马小定理
当p是质数时:
[a^{p-1}≡1(\%p)
]
将其变形一下即得
[a*a^{p-2}≡1(\%p)
]
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll qpow(ll a,ll b,ll p){
ll ans=1;
while(b){
if(b%2){
ans*=a;
ans%=p;
}
a*=a;
a%=p;
b>>=1;
}
return ans%p;
}
int main(){
int n,p;
cin>>n>>p;
for(int i=1;i<=n;i++){
cout<<(qpow(i,p-2,p)%p+p)%p<<endl;
}
}
得分:48。TLE
B' 欧拉定理
费马小定理只是欧拉定理的特殊情况。欧拉定理:
[a^{phi(p)}=1(\%p)
]
也就是说(a^{phi(p)-1})是(a)在(\%p)意义下的逆元。
其中(phi)是欧拉函数:小于n的正整数中与n互质的数的数目。
通式:(phi(n)=nPi_{i=1}^{n}left(1-frac{1}{p_i}
ight))
递推性质:
- (phi(p)=p-1) (p是质数)
- (phi(ab)=phi(b)phi(b)) (a,b互质)
- (phi(ip)=pphi(i)) (i是p的倍数)
所以可以用类似线性筛的方法求出欧拉函数表。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll qpow(ll a,ll b,ll p){
ll ans=1;
while(b){
if(b%2){
ans*=a;
ans%=p;
}
a*=a;
a%=p;
b>>=1;
}
return ans%p;
}
ll n,p;
ll phi[20000529],prime[2000052],ps;
bool mark[2000052];
void calc(){
phi[1]=1;
for(int i=2;i<=p;i++){
if(!mark[i]){
prime[++ps]=i;
phi[i]=i-1;
}
for(int j=1;j<=ps;j++){
if(i*prime[j]>p)break;
mark[i*prime[j]]=1;
if(i%prime[j]==0){
phi[i*prime[j]]=phi[i]*prime[j];
break;
}else{
phi[i*prime[j]]=phi[i]*(prime[j]-1);
}
}
}
}
int main(){
cin>>n>>p;
calc();
for(int i=1;i<=n;i++){
cout<<(qpow(i,phi[p]-1,p)%p+p)%p<<endl;
}
}
得分:32。TLE
C 神奇的递推式(正解)
设
[p=ka+r
]
然后放到(\%p)意义下
[ka+r=0(\%p)
]
两边同时乘以(a^{-1}r^{-1})
[kr^{-1}+a^{-1}=0(\%p)
]
所以
[a^{-1}=-kr^{-1}=-left[frac{p}{a}
ight]*(p\%a)^{-1}
]
转换成代码就是
inv[a]=-(p/a)*inv[p%a];
正解代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int n,p;
int inv[3000000];
int main(){
scanf("%d%d",&n,&p);
inv[1]=1;
printf("1
");
for(int i=2;i<=n;i++){
inv[i]=((-((ll)p/i)*inv[p%i])%p+p)%p;
printf("%d
",inv[i]);
}
}