Appearance
由于“同余”的篇幅过大,模意义的一些基础概念单开一页。
阶:
若正整数
性质
在模 意义下两两不同
逆元:
若正整数
求解
求解逆元等价于解同余方程
时间复杂度:
预处理 个数的逆元
预处理 模质数 的逆元:
线性递推即可,
注:若
预处理任意 个数模质数 的逆元:
令
时间复杂度:
通常使用这种方式预处理阶乘和阶乘的逆元
在线逆元
科技。
预处理时间复杂度:
大致做法是,对于
考虑分块,
可以证明这样对于上述范围下的
枚举
cpp
const int N = (1 << 21) | 1;
const int B = 300; // 需要满足 B^3 >= mod
int res1[N], res2[N], iv[N], _iv[N];
struct Barrett {
enum { s = 96 };
static constexpr u128 s2 = u128(1) << s;
u32 m;
u128 ivm;
void init(u32 m_) { m = (m_), ivm = ((s2 - 1) / m + 1); }
u32 div(u64 a) const { return a * ivm >> s; }
u32 calc(u64 a) const { return a - u64(div(a)) * m; }
} barret; // 顺带实现了 barrett 快速取模,不能处理模数为 2 的情况
void init(int mod) {
int lim = mod / B;
int _lim = -lim + mod;
for (int u = 1; u <= B; u++) {
int d = u * B;
int a = 0;
for (int i = 0; i <= lim; i++) {
if (a <= lim) {
res1[i] = u;
} else if (a >= _lim) {
res1[i] = -u;
} else {
int r = (_lim - a - 1) / d;
a += r * d;
i += r;
}
a += d;
if (a >= mod)
a -= mod;
}
}
for (int a = 0; a <= lim; a++)
res2[a] = barret.calc((a * B) * (res1[a] + mod));
iv[1] = 1;
for (int i = 2; i < 2 * B * B + 1; i++)
iv[i] = barret.calc(iv[mod % i] * (mod - mod / i));
for (int i = 1; i < 2 * B * B + 1; i++)
_iv[i] = mod - iv[i];
}
int inv(int x, int mod) {
if (mod < B)
return iv[x];
int a = x / B, b = x % B;
int u = res1[a];
int v = (res2[a] + b * u);
if (v < 0)
return barret.calc((u + mod) * _iv[-v]);
return barret.calc((u + mod) * iv[v]);
}
barret.init(p);
init(p);