[模板]乘法逆元
本博客所有代碼基於題目 luogu_P3811
逆元:
一般用於求 (a/b) mod p
定義:
若 a*x ≡ 1 (mod p) ,且 a 與 p 互質,那麽我們就能定義: x 為 a 的逆元,記為 a^-1 ,所以我們也可以稱 x 為 a 的倒數(mod p意義下)。
所以對於 (a/b) mod p ,我們就可以求出 b 在 mod p 意義下的逆元,然後乘上 a ,再 mod p ,就是這個乘法逆元的值了。
求法:
First:費馬小定理
定理內容:如果 a , p 互質,那麽 a^(p-1) ≡ 1 (mod p)
結合逆元方程 a*x ≡ 1 (mod p) ,得到 a*x ≡ a^(p-1) (mod p)
根據同余的性質,若p為質數,得到 x ≡ a^(p-2) (mod p)
即 x = a^(p-2) mod p, 快速冪 求解即可
Second:歐拉定理
定理內容:如果a,p互質,那麽a^φ(p) ≡ 1 (mod p),當 p 為質數時,φ(p)=p-1。
同理,結合同余方程,得 x=a^(p-2) mod p,快速冪求解即可
(這只是兩種不同的證明,代碼是相同的)
1 #include<cstdio> 2 #define ll long long 3 using namespace std; 4 int n,p; 5 inline ll ksm(ll a,ll b){TLE_83分6 ll ans=1; 7 a%=p; 8 while(b){ 9 if(b&1) ans=ans*a%p; 10 a=a*a%p; 11 b>>=1; 12 } 13 return ans%p; 14 } 15 void write(ll x){ 16 if(x<0) putchar(‘-‘),x=-x; 17 if(x>9) write(x/10);putchar(x%10^48); 18 } 19 int main(){ 20 scanf("%d%d",&n,&p); 21 for(int i=1;i<=n;i++) 22 write(ksm(i,p-2)),putchar(‘\n‘); 23 return 0; 24 }
Third:解不定方程
解同余方程 ax≡1 (mod p) 等價於 解不定方程 ax+py=1
Exgcd求解即可(不會 請左轉 Exgcd )
由於 p 是質數,那麽gcd(a,p)=1;
即求解不定方程 ax+by=gcd(a,b);
1 #include<cstdio> 2 using namespace std; 3 int x,y; 4 void exgcd(int a,int b){ 5 if(!b){x=1,y=0;return ;} 6 exgcd(b,a%b); 7 int t=x; 8 x=y,y=t-a/b*y; 9 } 10 void write(int x){ 11 if(x>9) write(x/10); 12 putchar(x%10^48); 13 } 14 int main(){ 15 int n,p; 16 scanf("%d%d",&n,&p); 17 for(int i=1;i<=n;++i) 18 exgcd(i,p),write((x%p+p)%p),putchar(‘\n‘); 19 return 0; 20 }AC_1240ms
Forth:線性遞推
復雜度 O(n)
遞推過程:
令 p=ki+r ; {k=[p/i] (下取整), r=p mod i } (i<p,k<p,r<i)
則有 ki+r≡0(mod p) ①
①式左右同乘i^-1*r^-1 得:
k*r^-1+i^-1 ≡ 0 (mod p)
移項 得
i^-1 ≡ -k*r^-1 (mod p)
帶入 k=[p/i] (下取整), r=p mod i;
i^-1 ≡ -[p/i]*(p mod i)^-1 (mod p) ②
由於 (p mod i) < i ,所以,在求出 i^-1 之前,我們早已求出 (p mod i)^-1;
因此用數組 inv[i] 記錄i^-1 ( i 的逆元)
則 inv[i]=-p/i*inv[p%i]%p;
不要以為到這裏就結束了
因為我們需要保證 i^-1>0
所以,我們在②式右邊+p ( p mod p=0,答案不變)
即 inv[i]=p-p/i*inv[p%i]%p;
當然 inv[1]=1,inv[0]=tan90°(賦值為0);
而且 for循環 要從 2 開始,防止改變 inv[1] 的值;
至此,證畢。
1 #include<cstdio> 2 #define ll long long 3 using namespace std; 4 const int maxn=3e6+5; 5 ll inv[maxn]={0,1}; 6 int main(){ 7 int n,p; 8 scanf("%d%d",&n,&p); 9 printf("1\n"); 10 for(int i=2;i<=n;i++) 11 inv[i]=(ll)p-(p/i)*inv[p%i]%p,printf("%d\n",inv[i]); 12 return 0; 13 }AC_664ms
參考文獻:https://www.cnblogs.com/zjp-shadow/p/7773566.html
[模板]乘法逆元