数论 · 逆元
前言
针对比赛学的一点点逆元,在这里记录一下其中一种方法。
当然求逆元的方法有很多种,之后学到再回来写。 —— 2021.8.24
UPDATE
-
2021 - 11 - 25 填坑。
添加了两种求逆元的方法 + 修改了一些写得不好(
难看至极)的地方 -
2021 - 12 - 01 补充了欧拉定理的证明。
定义
若
则称
一、费马小定理 + 快速幂
若 p 为质数,且
带入原柿子中就有:
然后直接用快速幂求解即可。
代码
省略快速幂。
inline int fermat (int a, int n)
{
return pow_mod (a, n - 2, n);
}
补:费马小定理证明
费马小定理实际上就是 欧拉定理(证明) 的一个特殊情况。
我们定义
例如:
欧拉定理就是:
而费马小定理就是当 p 为质数时的一个特殊情况 。
易证,当 p 为质数时,
所以当 p 为质数时,
二、扩展欧几里得
这样就可以直接用扩欧求逆元了。
但对于一次性求许多逆元(如洛谷的模板),效率并不高。
数论·扩欧
代码
省略扩欧代码。
inline int exgcd_inv (int a, int n)
{
int gcd, x, y;
gcd = exgcd (a, n);//求解方程 ax + ny = gcd (a, n)
return d == 1 ? (x + n) % n : -1;
}
三、线性求逆元
首先,有
再设
这样就有
可化简得到:
易知:
带入上柿可得:
这样就可以用递推求出逆元了。
代码
建数组
inv[i] = -(p / i) * inv[p % i];
用这个方法过了模板题的代码:
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define rint register int
const int maxn = 3e6 + 5;
int inv[maxn], n, q;
signed main ()
{
scanf ("%lld %lld", &n, &q);
inv[1] = 1;
printf ("1\n");
for (rint i (2); i <= n; ++i)
{
inv[i] = ((-(q / i) * inv[q % i]) % q + q) % q;
printf ("%lld\n", inv[i]);
}
return 0;
}
——