1. 程式人生 > >[模板]乘法逆元

[模板]乘法逆元

結束 urn 0ms null tdi -m class long main

本博客所有代碼基於題目 luogu_P3811

逆元:

  一般用於求 (a/b) mod p

定義:

  若 a*x 1 (mod p) ,且 ap 互質,那麽我們就能定義: x a 的逆元,記為 a^-1 ,所以我們也可以稱 xa 的倒數(mod p意義下)。

  所以對於 (a/b) mod p ,我們就可以求出 bmod 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){
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 }
TLE_83分

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

[模板]乘法逆元