乘法逆元
就是
此時b就是a模p意義下的逆元,即
下面我們用inv[a]表示a模p意義下的逆元。
逆元是好東西啊
有時候我們需要算出 a/b mod p 的值,用朴素的方法,我們只能在 a 上不斷加 p ,直到它能被 b 整除為止。
當 a,b,p 都很大的時候,這種方法就只能涼涼了,但如果有了逆元,我們就可以非常方便,快捷地求解。
——————某位大佬的話
所以我先講講逆元性質:
唯一性就不用講了
1.積性
假如a與b互質,
2.乘變除
證明如下:
至於逆元的求法,有很多,我只講四種。
首先,是求單個逆元
1.費馬小定理
當 p 為素數時:
所以
所以 就是a在模p意義下的逆元,快速冪求出
即可。
此方法的局限就是p只能是質數。
1 int ksm(int t) 2 { 3 if(t==1)return n%p; 4 if(t%2==0) 5 return ksm(t>>1)*ksm(t>>1)%p; 6 else 7 return ksm(t>>1)*ksm(t>>1)*(n)%p; 8 } 9 int main() 10 { 11 scanf("%d%d",&n,&p); 12 printf("%d",ksm(p-2)); 13 }
2.擴展歐幾里得
變一下,我們把p移到左邊就成了
因為y是變量所以可以變成這樣:
我們把設成x,就變成了擴歐的標准式:
就可以瞎搞了,至於不知道怎么瞎搞的同學,可以先點這里exgcd入門以及同余基礎
1 inline void exgcd(ll a,ll b) 2 { 3 if(!b) 4 {x=1;y=0;return;} 5 exgcd(b,a%b); 6 k=x;x=y; 7 y=k-a/b*y; 8 return; 9 } 10 int main() 11 { 12 scanf("%d%d",&n,&p); 13 exgcd(n,p); 14 printf("%d",x); 15 }
然后,就是求多個逆元
1.歐拉函數篩法
在數論,對正整數n,歐拉函數是小於或等於n的正整數中與n互質的數的數目(φ(1)=1)。 ——————百度百科
我們設表示小於等於x且與x互質的正整數的個數。
通式如下:
還有歐拉函數有如下幾點性質:
- 歐拉函數是積性函數——若m,n互質,
- 假如n為奇數,
- 假如n為質數,
歐拉函數與費馬小定理有點不明不白的關系
對任何兩個互質的正整數a, m(m>=2)有
這就是歐拉定理
所以類比於費馬小定理的證法:
所以
所以我們要求的就是,又因為歐拉函數是積性函數,所以把m篩一下質數就好了。
不過有更好的辦法,這里就不寫代碼了(其實不會)
2.線性遞推
證明好麻煩啊,那就不證了
反正就是設;
再往代
最后得出:
講完了,現在講題目
我要講洛谷的兩道題:
這就是模板題,直接套線性遞推,就能ac了
1 int main() 2 { 3 int n,m; 4 read(n),read(m); 5 inv[1]=1; 6 for(register int i=2;i<=n;i++) 7 inv[i]=(long long)(m-m/i)*inv[m%i]%m;\\加一個m是為了去負 8 for(register int i=1;i<=n;i++) 9 printf("%d\n",inv[i]); 10 }
值得一提的是,這題擴歐卡一卡也可以過(好水)
1 long long k,x,y; 2 inline void read(long long &x) 3 { 4 x=0; 5 int f=1; 6 char ch=getchar(); 7 while(ch<'0'||ch>'9') 8 { 9 if(ch=='-') 10 f=-1; 11 ch=getchar(); 12 } 13 while(ch>='0'&&ch<='9') 14 { 15 x=x*10+ch-'0'; 16 ch=getchar(); 17 }x*=f; 18 } 19 inline void exgcd(int a,long long b) 20 { 21 if(b==0) 22 {x=1;y=0;return;} 23 exgcd(b,a%b); 24 k=x;x=y; 25 y=k-a/b*y; 26 return; 27 } 28 int main() 29 { 30 long long n,m; 31 read(n),read(m); 32 for(register int i=1;i<=n;i++) 33 { 34 exgcd(i,m); 35 if(x<=0)x+=m; 36 printf("%lld\n",x); 37 } 38 }
對了,還有一個,費馬小定理算法。
得分48,a了3個,t了3個
1 inline long long ksm(int t,int i)//記得開long long不然第三個點會wa 2 { 3 if(t==1)return i%m; 4 if(t%2==0) 5 return ksm(t>>1,i)*ksm(t>>1,i)%m; 6 else 7 return ksm(t>>1,i)*ksm(t>>1,i)*(i)%m; 8 } 9 int main() 10 { 11 long long n; 12 io::begin(); 13 io::read(n); 14 io::read(m); 15 for(register int i=1;i<=n;i++) 16 printf("%lld\n",ksm(m-2,i)); 17 }
但是我發現,去掉 inline,就a了4個點,得分64.
所以給大家一個忠告,在遞歸函數下,inline不是一個好的卡常方法!
因為使用內聯函數后雖然調用函數的開銷降低了,但是有利必有弊,內聯函數會導致主函數指令增多、函數體積增大等情況。
這題就這樣
一看數據范圍還是很嚇人的,,怕不是要打高精,后來發現不是這樣,不過題解中真有高精算法,大佬!!
首先,先看我最開始引用的話,嗯,有點道理
然后解析題目發現:本題就是求b在mod p的意義下的逆元。
所以擴歐一遍過,只是讀入時注意一下邊讀邊模就好了;
1 inline void exgcd(int a,int b) 2 { 3 if(!b) 4 {x=1;y=0;pos=a;return;} 5 exgcd(b,a%b); 6 k=x;x=y; 7 y=k-a/b*y; 8 return; 9 } 10 int main() 11 { 12 std::cin>>a1>>b1; 13 int n=0,m=0; 14 for(register int i=0;i<strlen(a1);i++) 15 n=(n*10+a1[i]-48)%mod; 16 for(register int i=0;i<strlen(b1);i++) 17 m=(m*10+b1[i]-48)%mod; 18 exgcd(m,mod); 19 if(pos!=1)printf("Angry!"); 20 else 21 { 22 if(x<0)x+=mod; 23 printf("%lld",(1ll*n*x)%mod); 24 } 25 }
最后通過這道題的大質數給大家念一首詩:苟利國家生死以