洛谷P3811乘法逆元
定义:乘法逆元又称数论倒数。若 且a,m互质,则x为a的逆元,记为,若a,m不互质,则不存在逆元。当且仅当m为素数时,a有唯一的乘法逆元。
用途:在模算术中,有以下几个公式:
(a+b) mod p =((a mod p)+(b mod p)) mod p
(a-b) mod p=((a mod p) – (b mod p) + p) mod p
ab mod p=(a mod p)(b mod p)mod p
在模意义下的运算中,这几个公式可以降低运算过程中数据的规模,然而,遇到除法运算时,便会出现问题。下面是个错误的式子:a/b mod p=(a mod p)/(b mod p) mod p 可以举例证伪。在一系列模运算中,被除数和除数可能进行过取模操作,因此无法直接得到正确结果。因此,模意义下的除法要转化成乘法。
一个例子:6/2=3(mod 5) 6*3=3(mod 5)
求逆元方法一(费马小定理):
费马小定理:若p是质数,则对于任意整数a,有 ,。
结合快速幂就可以求逆元。缺陷:这种算法必须保证p是质数,而且复杂度较高,不适合批量操作。
#include
using namespace std;
typedef long long ll;
ll n,p;
inline ll quick_pow(ll a,ll b)
{
ll ans=1;
for( ;b;b>>=1)
{
if(b&1) ans=ans*a%p;
a=a*a%p;
}
return ans;
}
int main()
{
scanf("%lld%lld",&n,&p);
for(ll i=1;i<=n;i++) printf("%lld\n",quick_pow(i,p-2));
return 0;
}
求逆元方法二(扩展欧几里得算法):即求同余方程 的解(通常求最小正整数解)。这个算法只要求a,p互质,速度也更快,但也不适合批量操作。
#include
using namespace std;
typedef long long ll;
ll n,p,x,y;
void exgcd(ll a,ll b,ll &x,ll &y)
{
if(!b) {x=1,y=0; return;}
exgcd(b,a%b,x,y);
ll z=x; x=y; y=z-(a/b)*y;
}
int main()
{
scanf("%lld%lld",&n,&p);
for(ll i=1;i<=n;i++)
{
exgcd(i,p,x,y);
x=(x%p+p)%p;
printf("%lld\n",x);
}
return 0;
}
求逆元方法三(线性递推):推导过程如下(第2行到第3行,两边同时乘i和p mod i的逆元)
#include
using namespace std;
typedef long long ll;
const int maxn=3e6+5;
ll n,p;
ll inv[maxn]; //inv[i]表示i的逆元
int main()
{
scanf("%lld%lld",&n,&p);
inv[1]=1; printf("1\n");
for(ll i=2;i<=n;i++)
{
inv[i]=(p-p/i)*inv[p%i]%p;
printf("%lld\n",inv[i]);
}
return 0;
}