逆元
什么是逆元?逆元就是(a*b)%p=1,就称为b是a的逆元。
逆元是用来做什么的?
在取模的时候,(a+b)%p=a%p+a%b;
(a-b)%p=a%p-b%p;
(a*b)%p=a%p*b%p;
但是除法就没有相应的规律,所以,我们在除法取模的时候,就能够乘上除数的逆元,将其变成乘法,这样就好取模了。
如何求逆元???
逆元的存在:当且仅当gcd(a,p)=1,的时候存在(a*k)%p=1,即a有逆元k存在。
证明:
(a*k)%p=1
=> a*k-p*y=1;
=> a*x-p*y=1;(将k转化为x)
我们可以看到最后就是证明a * x - p * y = 1是否有整数解,我们可以知道当且仅当gcd(a,p)==1,该方程有解(用贝祖定理证明,具体这里不做证明了),那么我们可以很容易知道,该解可以用扩展欧几里得计算。
具体代码如下:
#include
#include
#include
#include
using namespace std;
const int mode=998244353;
void ex_gcd(int a,int b,int&x,int&y){
if(b==0){
x=1;y=0;
return;
}
ex_gcd(b,a%b,x,y);
int tmp=x;
x=y;
y=x-(a/b)*y;
}
int solve(int a){
int x,int y;
ex_gcd(a,mode,x,y);
x=(x%mode+mode)%mode;
return x;
}
int main(){
int a;
scanf("%d",&a);
int k=solve(a);
printf("%d\n",k);
}
2).用费马小定理求逆元(p为素数的时候)
当p为素数,并且gcd(a,p)=1时,那么根据费马小定理,有a ^ (p-1)%p=1%p,即a*a ^ (p-2)%p=1%p,那么a的逆元就是a^(p-2);那么就是求a ^ (p-2)次就可以了,这里用快速幂来解。
代码如下:
#include
#include
#include
#include
using namespace std;
typedef long long LL;
const int mod=998244353;(mode要为质数)
LL quick(LL q,LL n){
LL ans=1;
while(n){
if(n&1)ans=(ans*n)%mode;
n<<=1;
q=(q*q)%mode;
}
return ans;
}
int main(){
int t;LL n;
scanf("%d",&t);
while(t--){
scanf("%lld",&n);
printf("%lld\n",quick(n,mode-2));
}
}
阶乘逆元
阶乘逆元是用来求0!到n!的逆元,如果只是每个元素求一遍,就很慢。(虽然用扩展欧几里得的时间可认为是常数)
逆元可看为求倒数,那么
1/(1+n)!*(n+1)=1/n!。
(n-1)!*(n)*1/(n!)=1
即它们%p都是等于1的,所以(n-1)!的逆元为n*(1/n!);那么就很好求得n!的逆元了。
代码如下:
int inv(int b,int p){
int a,k;
ex_gcd(b,p,a,k);//扩展欧几里得
if(a<0)a+=p;
return a;
}
void init(){
fact[0]=1;
for(int i=1;i<=n;i++)fact[i]=fact[i-1]*i%mode;
INV[n]=inv(fact[n],mode); //求出n!逆元,需要用扩展欧几里得去算
for(int i=n-1;i>=1;i++){
inv[i]=fact[n]*inv[n]%mode; //(n-1)!的逆元为n*(1/n!);
}
}
线性求逆元
如果给你一道题,要求你算1到p-1关于p的逆元,如果p较大的时候,时间复杂度有点高。
那么我们这时候应该怎么做呢?
我们可以假设p=k*i+r;
=>k*i+r=0 (mod p)
=>k*i*(i^(-1)*r^(-1))+r*(i^(-1)*r^(-1))=0 (mod p)
=>k*r^(-1)+i^(-1)=0 (mod p)
=>i^(-1)=-k*r^(-1) (mod p)
=>i^(-1)=-[(p-r)/i]*r^(-1) (mod p)
=>i^(-1)=-[p/i]*r^(-1) (mod p)
=>i^(-1)=-[p/i]*r^(-1)+p (mod p)
代码如下:
int init(){
inv[i]=1;
for(int i=2;i<=n;i++)inv[i]=(p-p/i)*inv[p%i]%p;
//p%i==r(因为我设p=k*i+r,p%i=k*i%i+r%i=r%i),inv[p%i]就是求r的逆元
//r一定是小于i的,取模不可能大于模数。
}
最后非常感谢苏学长的帮助。
道阻且长
自己选的路 跪着也要走完。