乘法逆元
乘法逆元
例题1
小凯的数字
一串数字l(l+1)(l+2).......(r-1)r,例如l=2,r=5,数字为2345,小凯很喜欢数字9,所以写下的数字除以9的余数是多少
因为模运算必须是整数,上面的\((l+r)(r-l+1)\)可以用整数计算,除以2就不一定还是整数了。
那么分数在模意义下该怎么表示呢?也就是说在mod p意义下,能不能表示\(\frac{1}{a}\)呢?
在实数意义下,对于任意的\(a\neq 0\),都有\(a\times \frac{1}{a}=1\),所以在模意义下,也希望1/a也满足同样的性质:\(a\times \frac{1}{a}\equiv 1(\mod p)\),也就是说,尝试找到一个整数,满足\(a\times x\equiv 1(\mod p)\)
定义
若\(a\times x\equiv1(\mod p)\),则称x为a在模p意义下的乘法逆元,记为\(a^{-1}(\mod p)\)
在本章种,模意义下的乘法逆元,简称为逆元。
所以这题只需要求出\(5(l+r)(r-l+1)\mod9\) 为了降低处理的数量级,可以\(5(l+r)\%9(r-l+1)\%9\mod9\)
#include <bits/stdc++.h>
using namespace std;
int t;
long long l,r,ans;
int main()
{
cin>>t;
while(t--){
cin>>l>>r;
ans=((l+r)%9*(r-l+1)%9*5)%9;
cout<<ans<<endl;
}
return 0;
}
接下来,就从两种不同的方法给定一组a,p,求出a在模p意义下的逆元
方法1
拓展欧几里得算法
从\(a\times x\equiv1(\mod p)\)入手,等价于\(\exists y,ax+py=1\)等价。实际上就是一个关于a,p的不定方程,就可以用拓展欧几里得算法来解决
并且由裴蜀定理,可以发现存在a在模p意义下的逆元当且仅当gcd(a,p)=1,a和p互质,即\(a\perp p\)
int inv(int a,int p){ //用exgcd求逆元
int x,y;
exgcd(a,p,x,y);
//求不定方程ax+py=1的一组解
return x;
//求出的x就是a在模p的意义下的乘法逆元
}
方法2
费马小定理
若p为质数,且a不是p的倍数,则\(a^{p-1}\equiv1(\mod p)\)
假设p为质数,那么如果a不是p的倍数,就有\(a\times a^{p-2}=a^{p-1}\equiv 1(\mod p)\)
所以a在模p意义下的逆元就是\(a^{p-2}\),可以用快速幂来解决。
如果a是p的倍数的话,对于任意x,ax也一定是p的倍数,不可能在模p的意义下等于1,也就不存在逆元了
int inv(int a,int p){ //用费马小定理求逆元
return pow(a,p-2,p);
//求出a^(p-2)mod p的值
}
不管什么方法,都可以求出一个a在模p的意义下的逆元
定理
在大于等于0,小于p的范围内,模p的逆元(若存在)是唯一的
例题2
乘法逆元(线性时间)
给定n,q,求1~n中所有整数在模p意义下的乘法逆元
递推
#include <bits/stdc++.h>
using namespace std;
int n,p;
long long inv[3000010];
int main()
{
scanf("%d%d",&n,&p);
inv[1]=1;
//递推初始化
for(int i=2;i<=n;i++)
inv[i]=(p-inv[p%i]*(p/i)%p);
//i^(-1)=p-(p%i)^(-1)*(p/i)%p
for(int i=1;i<=n;i++)
printf("%lld\n",inv[i]);
return 0;
}
//卡cin、cout
倒推
直接倒推可能会比较困难,但是我们知道\(\frac{1}{k}=\frac{(k-1)!}{k!}\),所以加入我们已经知道了1!,2!,...,n!mod p的结果,
并且已经知道了1!,2!,....,n!的逆元,就可以知道1到n的逆元了
时间复杂度为O(n+log n)
#include <bits/stdc++.h>
using namespace std;
int n,p;
long long inv[3000010],fac[3000010];
long long pow(long long x,int y,int p){
long long ans=1;
for(;y;y>>=1){
if(y&1) ans=ans*x%p;
x=x*x%p;
}
return ans;
}
int main()
{
scanf("%d%d",&n,&p);
fac[0]=1;
for(int i=1;i<=n;i++)
fac[i]=fac[i-1]*i%p;
//求出1!,2!,...,n!
inv[n]=pow(fac[n],p-2,p);
//求出(n!)^(-1)
for(int i=n-1;~i;i--){
inv[i]=inv[i+1]*(i+1)%p;
}
//求出((n-1)!^(-1),(n-2)!^(-1),...,1!^(-1))
for(int i=1;i<=n;i++)
printf("%lld\n",fac[i-1]*inv[i]%p);
//求出1^(-1),2^(-1),...,n^(-1)
return 0;
}