[洛谷P3811]【模板】乘法逆元

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)=n\Pi_{i=1}^{n}\left(1-\frac{1}{p_i}\right)\)
递推性质:

  1. \(\phi(p)=p-1\) (p是质数)
  2. \(\phi(ab)=\phi(b)\phi(b)\) (a,b互质)
  3. \(\phi(ip)=p\phi(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}\right]*(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\n");
	for(int i=2;i<=n;i++){
		inv[i]=((-((ll)p/i)*inv[p%i])%p+p)%p;
		printf("%d\n",inv[i]);
	}
}
posted @ 2019-02-24 20:38  water_lift  阅读(231)  评论(0编辑  收藏  举报