# 模逆元與應用
如有錯誤請告知,感謝><
## 模逆元定義
在乘法中,我們定義$a$的倒數為$a^{-1}$,即$a\times a^{-1}=1$
對任意正整數$a$,取模($mod\space p$)後的倒數定義成$a$的模倒數或稱模逆元(在$mod\space p$的情況下)
即$a\times a^{-1}=1(mod\space p)$,此時的$a^{-1}$為模逆元
但對於任意的$a$、$p$,其不一定有模逆元素,若$gcd(a,p)\space !=1 \space, a\space mod\space p$即無模逆元
## 模逆元算法
### 根據費馬小定理:
#### 若p為質數,對任意正整數$a$,$(a^{p-2}mod\space p)$是$a$在$[1, P-1]$區間的唯一乘法反元素
所以我們可以搭配快速冪在$O(log\space p)$內算出模逆元
## 線性模逆元
若我們想要求 $1, 2, 3, \dots n$ 的模逆元,一個一個套用費馬小定理顯然有點慢,以下介紹一個 $O(n)$ 的算法。
首先,顯然 $1^{-1} \equiv 1\pmod p$。
其餘我們遞迴求解,假設現在要求 $i^{-1}$,令 $k = \lfloor \frac{p}{i} \rfloor$, $j = p\bmod i$,有 $p = ik + j$。
放到$\bmod p$ 下會得到 $ik + j \equiv 0\pmod p$,兩邊同乘 $i^{-1}\times j^{-1}$:
$j^{-1}k + i^{-1} \equiv 0\pmod p$
$i^{-1} \equiv -kj^{-1}\pmod p$
$i^{-1} \equiv -k\times {(p\bmod i)}^{-1}\pmod p$
$i^{-1} \equiv -\lfloor \frac{p}{i} \rfloor \times {(p\bmod i)}^{-1}\pmod p$
觀察到 $p \bmod i < i$,遞迴時完全可以假設我們算 $i$ 時已經知道所有 $j < i$ 的模逆元 $j^{-1}$,也就是說我們推出了模逆元的遞迴式,以迭代實作。
$$i^{-1} = \begin{cases}
1&:n=1\\
-\lfloor \frac{p}{i} \rfloor \times {(p\bmod i)}^{-1}&:\text{Otherwise}
\end{cases}$$
```cpp=
inv[1] = 1;
for(int i=2;i<=n;i++)
{
inv[i] = (long long)(p - p/i) * inv[p%i] % p;
}
```
用 $p - \lfloor \frac{p}{i} \rfloor$ 防止出現負數。
注意到 inv[0] 是未定義的,若遇到 $i\mid p$ 的情況,程式會使用到 inv[0],因為 $i\mid p$ 時 $i^{-1}$ 不存在。
(此章節參考自[OI-wiki 乘法逆元](https://oi-wiki.org/math/number-theory/inverse/#线性求逆元))
## 模逆元應用
可快速算出$C_m^n$
$C_m^n=\frac {n!}{m!\times (n-m)!}$
$C_m^n=pre[n]\times pre_i[m]\times pre_i[n-m]$
:::spoiler code
```cpp=
#include<bits/stdc++.h>
#define Crbubble cin.tie(0); ios_base::sync_with_stdio(false);
#define ll long long
using namespace std;
const ll MAXN = 100005;
const ll mod = 998244353;
ll pre[MAXN];
ll pre_i[MAXN];
ll inv[MAXN];
ll f_pow(ll a, ll b)
{
ll base = a, cou = 1;
while(b)
{
if(b & 1) cou *= base;
base *= base;
cou %= mod;
base %= mod;
b /= 2;
}
return cou;
}
ll f_inv(ll n)
{
return f_pow(n,mod-2);
}
void build(ll n)
{
pre[0] = pre[1] = 1;
pre_i[0] = pre_i[1] = 1;
inv[0] = inv[1] = 1;
for(int i=2;i<=n;i++)
{
pre[i] = pre[i-1] * i % mod;
inv[i] = f_inv(i);
pre_i[i] = pre_i[i-1] * inv[i] % mod;
}
}
ll C(ll n, ll m)
{
return pre[n] * pre_i[m] % mod * pre_i[n-m] % mod;
}
int main()
{ Crbubble
build(MAXN);
cout << inv[4] << endl;
cout << C(6,3) << endl;
return 0;
}
```
:::
## 例題(?)
- [Codeforces 1546D](https://codeforces.com/contest/1546/problem/D) D - AquaMoon and Chess
:::spoiler 想法
可以觀察到兩個1可以配成一組,我們只需算出共有$m$組1,然後把1插入0(設有$n$個0)之間,$C_{m}^{n+m}$即為答案
:::
{"metaMigratedAt":"2023-06-16T04:15:17.543Z","metaMigratedFrom":"Content","title":"模逆元與應用","breaks":true,"description":"如有錯誤請告知,感謝><","contributors":"[{\"id\":\"83a19121-6ed9-413f-9a22-b0ab26ee4fd0\",\"add\":1290,\"del\":45},{\"id\":\"e885d082-17b5-48b8-ac15-dc73ae9ea8d9\",\"add\":1303,\"del\":11}]"}