洛谷 P3811 【模板】模意义下的乘法逆元-普及/提高-
题目背景
这是一道模板题
题目描述
给定 n,pn,pn,p 求 1∼n1\sim n1∼n 中所有整数在模 ppp 意义下的乘法逆元。
这里 aaa 模 ppp 的乘法逆元定义为 ax≡1(modp)ax\equiv1\pmod pax≡1(modp) 的解。
输入格式
一行两个正整数 n,pn,pn,p。
输出格式
输出 nnn 行,第 iii 行表示 iii 在模 ppp 下的乘法逆元。
输入输出样例 #1
输入 #1
10 13
输出 #1
1
7
9
10
8
11
2
5
3
4
说明/提示
$ 1 \leq n \leq 3 \times 10 ^ 6,,,n < p < 20000528 $。
输入保证 $ p $ 为质数。
solution
思路: 求乘法逆元的方式有很多中
- 1 可以用拓展欧几里得算法直接求解方程 ax=1 mod p
- 2 可以用费马小定理 a^p-1=1 即 a^p-2 即为a的逆元
- 3 前两个算法对于求单个逆元都是比较快的,但是如果要求 [1, n] 所有数的逆元,可以用递推公式实现线性时间复杂度
例如求 i 的逆元 p = ki + r- 3.1 ki + r = 0 mod p
两边乘以 i^-1 * r^-1 得到 - 3.2 kr^-1 + i^-1 = 0 mod p 即 i^-1 = -kr^-1 mod
其中 r 必然比 i 小,所以从小到大递推必然可以
易得 1 * 1 = 1 mod p
- 3.1 ki + r = 0 mod p
- 4 可以用阶乘逆元
- 用 f[n] 表示 n! 的逆元, 则
- (1) f[n] * n! = 1
- (2) f[n-1] * (n-1)! = 1
- 将(2)代入(1)中, 即 f[n] * n = f[n-1]
- n^-1 = f[n] * (n-1)!
具体做法是先求 g[i] = i!, 然后求 f[n] = g[n]^-1 (可以用快速幂), 然后逆着推求 f[i]
然后 i^-1 = f[n] * g[n-1]
这个也是线性的,但是比较麻烦,其主要用于求阶乘的逆元
- n^-1 = f[n] * (n-1)!
- 用 f[n] 表示 n! 的逆元, 则
代码
#include "cstring"
#include "string"
#include "algorithm"
#include "iostream"
#include "vector"
#include "unordered_set"
#include "unordered_map"
#include "bitset"
#include "queue"
#include "set"using namespace std;/** 思路:求乘法逆元的方式有很多中* 1 可以用拓展欧几里得算法直接求解方程 ax=1 mod p* 2 可以用费马小定理 a^p-1=1 即 a^p-2 即为a的逆元* 3 前两个算法对于求单个逆元都是比较快的,但是如果要求 [1, n] 所有数的逆元,可以用递推公式实现线性* 例如求 i 的逆元 p = ki + r* 3.1 ki + r = 0 mod p* 两边乘以 i^-1 * r^-1 得到* 3.2 k*r^-1 + i^-1 = 0 mod p 即 i^-1 = -k*r^-1 mod p* 其中 r必然比 i 小,所以从小到大递推必然可以* 易得 1 * 1 = 1 mod p* 4 可以用阶乘逆元* 用 f[n] 表示 n! 的逆元, 则* (1) f[n] * n! = 1* (2) f[n-1] * (n-1)! = 1* 将(2)代入(1)中, 即 f[n] * n = f[n-1]* n^-1 = f[n] * (n-1)!* 具体做法是先求 g[i] = i!, 然后求 f[n] = g[n]^-1 (可以用快速幂), 然后逆推求 f[i]* 然后 i^-1 = f[n] * g[n-1]* 这个也是线性的,但是比较麻烦,其主要用于求阶乘的逆元*/const int N = 3e6 + 5, M = 2e6, INF = 1e9, MOD = 998244353;
typedef long long ll;int n, inv[N], p;int main() {scanf("%d%d", &n, &p);inv[1] = 1;printf("%d\n", 1);for (int i = 2; i <= n; i++) {//i^-1 = -k*r^-1 mod pinv[i] = p - 1ll * p / i * inv[p % i] % p;printf("%d\n", inv[i]);}return 0;}