题解 P3811 【【模板】乘法逆元】

Rising_Date

2018-08-18 18:03:22

Solution

## 逆元:   一般用于求 $\frac{a}{b}\ \;mod\; p$ ## 定义:   若 $a*x ≡ 1 \;(mod \;p)$ ,且 $a$ 与 $p$ 互质,那么我们就能定义: $x$ 为 $a$ 的逆元,记为 $a^{-1}$ ,所以我们也可以称 $x$ 为 $a$ 的倒数( $mod \;p$ 意义下)。   所以对于 $\frac{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$, **快速幂** 求解即可 _(这只是两种不同的证明,代码是相同的)_ ```cpp //TLE_83分 #include<cstdio> #define ll long long using namespace std; int n,p; inline ll ksm(ll a,ll b){ ll ans=1; a%=p; while(b){ if(b&1) ans=ans*a%p; a=a*a%p; b>>=1; } return ans%p; } void write(ll x){ if(x<0) putchar('-'),x=-x; if(x>9) write(x/10);putchar(x%10^48); } int main(){ scanf("%d%d",&n,&p); for(int i=1;i<=n;i++) write(ksm(i,p-2)),putchar('\n'); return 0; } ``` ### Third:解不定方程   **解同余方程** $ax≡1 \;(mod \;p)$ 等价于 **解不定方程** $ax+py=1$   **Exgcd**求解即可(不会 请左转 [](https://www.cnblogs.com/RisingGods/p/9497928.html) _Exgcd_ )    $\;\;\;\;\;\;$由于 $p$ 是质数,那么$gcd(a,p)=1$;    $\;\;\;\;\;\;$即求解不定方程 $ax+by=gcd(a,b)$; ```cpp //AC_1240ms #include<cstdio> using namespace std; int x,y; void exgcd(int a,int b){ if(!b){x=1,y=0;return ;} exgcd(b,a%b); int t=x; x=y,y=t-a/b*y; } void write(int x){ if(x>9) write(x/10); putchar(x%10^48); } int main(){ int n,p; scanf("%d%d",&n,&p); for(int i=1;i<=n;++i) exgcd(i,p),write((x%p+p)%p),putchar('\n'); return 0; } ``` ### Forth:线性递推 #### 复杂度 $O(n)$ #### 递推过程:   令 $p=ki+r$ ; {$\;k=\big\lfloor\frac pi\big\rfloor$, $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=\big\lfloor\frac pi\big\rfloor$ , $r=p\; mod\; i$;    $\;\;\;\;\;\;$ $i^{-1}$ ≡ $\;-\big\lfloor\frac pi\big\rfloor$ $*(p\; mod \;i)^{-1} \;\;(mod\; p)$ ②   由于 $(p\; mod\; i) < i$ , $\;\;\;\;\;\;$ 所以,在求出 $i^{-1}$ 之前,我们早已求出 $(p\; mod \;i)^{-1}$;    $\;\;\;\;\;\;$ 因此用数组 $inv[i]$ 记录$i^{-1}$ ( $i$ 的逆元)    $\;\;\;\;\;\;$ 则$inv[i]=-\frac{p}{i}\ * inv[p\;mod\;i]\;mod\;p$ ;  不要以为到这里就结束了    $\;\;\;\;\;\;$ 因为我们需要保证 $i^{-1}>0$   $\;\;\;\;\;\;$ 所以,我们在②式右边$\;+p\; $( $p\;mod\; p=0$, 答案不变)    $\;\;\;\;\;\;$ 即 $inv[i]=p-\frac{p}{i}\ * inv[p\;mod\;i]\;\;mod\;p$;    $\;\;\;\;\;\;$ 当然 $inv[1]=1,inv[0]=tan90$°(赋值为$0$);    $\;\;\;\;\;\;$而且$for$循环 要从$2$开始,防止改变 $inv[1]$ 的值;   至此,证毕。 ```cpp //AC_664ms #include<cstdio> #define ll long long using namespace std; const int maxn=3e6+5; ll inv[maxn]={0,1}; int main(){ int n,p; scanf("%d%d",&n,&p); printf("1\n"); for(int i=2;i<=n;i++) inv[i]=(ll)p-(p/i)*inv[p%i]%p,printf("%d\n",inv[i]); return 0; } ```