逆元詳解


今天我們來探討逆元在ACM-ICPC競賽中的應用,逆元是一個很重要的概念,必須學會使用它。

 

對於正整數,如果有,那么把這個同余方程中的最小正整數解叫做的逆元。

 

逆元一般用擴展歐幾里得算法來求得,如果為素數,那么還可以根據費馬小定理得到逆元為

 

推導過程如下

                            

 

求現在來看一個逆元最常見問題,求如下表達式的值(已知

 

           

 

當然這個經典的問題有很多方法,最常見的就是擴展歐幾里得,如果是素數,還可以用費馬小定理。

 

 

但是你會發現費馬小定理和擴展歐幾里得算法求逆元是有局限性的,它們都會要求互素。實際上我們還有一

種通用的求逆元方法,適合所有情況。公式如下

 

          

 

現在我們來證明它,已知,證明步驟如下

 

          

 

接下來來實戰一下,看幾個關於逆元的題目。

題目:http://poj.org/problem?id=1845

 

題意:給定兩個正整數,求的所有因子和對9901取余后的值。

 

分析:很容易知道,先把分解得到,那么得到,那么

     的所有因子和的表達式如下

 

    

 

所以我們有兩種做法。第一種做法是二分求等比數列之和。

第二種方法就是用等比數列求和公式,但是要用逆元。用如下公式即可

 

                     

 

因為可能會很大,超過int范圍,所以在快速冪時要二分乘法。

其實有些題需要用到的所有逆元,這里為奇質數。那么如果用快速冪求時間復雜度為

如果對於一個1000000級別的素數,這樣做的時間復雜度是很高了。實際上有的算法,有一個遞推式如下

 

                   

 

它的推導過程如下,設,那么

 

       

 

對上式兩邊同時除,進一步得到

 

       

 

再把替換掉,最終得到

 

       

 

初始化,這樣就可以通過遞推法求出模奇素數的所有逆元了。

 

另外的所有逆元值對應中所有的數,比如,那么對應的逆元是

 

 

題目:http://www.lydsy.com/JudgeOnline/problem.php?id=2186

 

題意:互質的數的個數,其中

 

分析:因為,所以,我們很容易知道如下結論

     對於兩個正整數,如果的倍數,那么中與互素的數的個數為

 

     本結論是很好證明的,因為中與互素的個數為,又知道,所以

     結論成立。那么對於本題,答案就是

 

     

 

      其中為小於等於的所有素數,先篩選出來即可。由於最終答案對一個質數取模,所以要用逆元,這里

      求逆元就有技巧了,用剛剛介紹的遞推法預處理,否則會TLE的。

 

代碼:

[cpp]  view plain  copy
 
 在CODE上查看代碼片派生到我的代碼片
  1. #include <iostream>  
  2. #include <string.h>  
  3. #include <stdio.h>  
  4. #include <bitset>  
  5.   
  6. using namespace std;  
  7. typedef long long LL;  
  8. const int N = 10000005;  
  9.   
  10. bitset<N> prime;  
  11.   
  12. void isprime()  
  13. {  
  14.     prime.set();  
  15.     for(int i=2; i<N; i++)  
  16.     {  
  17.         if(prime[i])  
  18.         {  
  19.             for(int j=i+i; j<N; j+=i)  
  20.                 prime[j] = false;  
  21.         }  
  22.     }  
  23. }  
  24.   
  25. LL ans1[N],ans2[N];  
  26. LL inv[N];  
  27.   
  28. int main()  
  29. {  
  30.     isprime();  
  31.     int MOD,m,n,T;  
  32.     scanf("%d%d",&T,&MOD);  
  33.     ans1[0] = 1;  
  34.     for(int i=1; i<N; i++)  
  35.         ans1[i] = ans1[i-1] * i % MOD;  
  36.     inv[1] = 1;  
  37.     for(int i=2;i<N;i++)  
  38.     {  
  39.         if(i >= MOD) break;  
  40.         inv[i] = (MOD - MOD / i) * inv[MOD % i] % MOD;  
  41.     }  
  42.     ans2[1] = 1;  
  43.     for(int i=2; i<N; i++)  
  44.     {  
  45.         if(prime[i])  
  46.         {  
  47.             ans2[i] = ans2[i-1] * (i - 1) % MOD;  
  48.             ans2[i] = ans2[i] * inv[i % MOD] % MOD;  
  49.         }  
  50.         else  
  51.         {  
  52.             ans2[i] = ans2[i-1];  
  53.         }  
  54.     }  
  55.     while(T--)  
  56.     {  
  57.         scanf("%d%d",&n,&m);  
  58.         LL ans = ans1[n] * ans2[m] % MOD;  
  59.         printf("%lld\n",ans);  
  60.     }  
  61.     return 0;  
  62. }  


 

接下來還有一個關於逆元的有意思的題目,描述如下

 

     

 

證明:

 

     

 

     其中

 

     所以只需要證明,而我們知道的逆元對應全部

     中的所有數,既是單射也是滿射。

 

     所以進一步得到

 

      

 

      證明完畢!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM