再谈数论

· · 算法·理论

由于本人是小升 xxs,数学学的不多,文章有错误还请多多谅解。

浅谈数论

由普及组二等小朋友变成了提高组三等小朋友 / 咦

使用 DeepSeek 优化了一下 Markdown。

二元一次不定方程

虽然是【模板】exgcd,但比 exgcd 难多了

对于不定方程 ax + by = c,求:

  • 若该方程有整数解,且有正整数解,求正整数解的数量、正整数解中 x 的最小值、正整数解中 y 的最小值、正整数解中 x 的最大值、正整数解中 y 的最大值。
  • 若该方程有整数解,没有正整数解,求整数解中 x 的最小正整数值、y 的最小正整数值。

显然,根据裴蜀定理,如果 \gcd(a, b) \nmid c,则方程无解。

::::info[裴蜀定理] 设 a, b 是不全为零的整数,那么,对于任意整数 x, y,都有 \gcd(a, b) \mid ax+by 成立,并且,存在整数 x, y,使得 ax + by = \gcd(a, b) 成立。

:::info[证明] 设 d = \gcd(a, b)。因为 d \mid a, b,所以存在整数 p, q,使得 a = pd, b = qd 成立,因此,总有:

ax + by = d(px + qy)

说明 d \mid ax + by

考虑 a, b\ne 0 的情况,如果 ab0,显然有 (x, y) = (1, 0)/(0, 1) 使得 ax + by = \gcd(a, b) 成立。

因为 \gcd(a, b) = \gcd(-a, b) = \gcd(a, -b),设 a, b > 0

考虑辗转相除:

\begin{cases} a = q_1b + r_1, & 0 \le r_1 < b \\ b = q_rr_1 + r_2, & 0 \le r_2 < r_1 \end{cases} \begin{cases} r_1= q_3r_2 + r_3, & 0 \le r_3 < r_2 \\ r_2= q_4r_3 + r_4, & 0 \le r_4 < r_3 \\ \dots \\ r_{n - 2} = q_nr_{n - 1} + r_n, & 0 \le r_{n - 1} < r_{n - 1} \\ r_{n - 1} = q_{n + 1}r_n, \end{cases}

因为最大公因数是 d,所以 r_n = d,根据倒数第二个等式,那么:

d = r_n = r_{n - 2} - q_nr_{n - 1}

根据倒数第三个等式:

r_{n - 1} = r_{n - 3} - q_{n - 1}r_{n - 2}

带入上式:

\begin{aligned} d &= r_{n - 2} - q_n(r_{n - 3} - q_{n - 1}r_{n - 2}) \\ &= (1 + q_nq_{n - 1})r_{n - 2} - q_nr_{n - 3} \end{aligned}

以此类推,逐步消去 r_{n - 2}, r_{n - 3}, \dots,可以得到:

d = xa + yb

证毕。 ::: ::::

首先,使用 exgcd 求出 ax + by = \gcd(a, b) 的解,记为 x_0, y_0,则有:

a\frac{x_0c}{\gcd(a, b)} + b\frac{y_0c}{\gcd(a, b)} = c

显然,我们找到了原方程的一组特解 x_1 = \frac{x_0c}{\gcd(a, b)}, y_1 = \frac{y_0c}{\gcd(a, b)}

我们设 a(x_1 + m) + b(y_1 + n) = c,考虑怎么求出 m, n

将式子展开可以发现,需要让 am + bn = 0

我们让 m = t \times \frac{b}{\gcd(a, b)}, n = t \times \frac{a}{\gcd(a, b)}(t \in \mathbb{Z}),带入发现 am + bn = 0,于是通解为:

\begin{cases} x = x_1 + t \times \frac{b}{\gcd(a, b)} \\ y = y_1 - t \times \frac{a}{\gcd(a, b)} \end{cases}

x 最小值

显然,我们要找出最小的 t 使得 x \ge 1

x \ge 1 \\ x_1 + t \times \frac{b}{\gcd(a, b)} \ge 1 \\ t \times \frac{b}{\gcd(a, b)} \ge 1 - x_1 \\ t \ge \left\lceil \frac{1 - x_1}{\frac{b}{\gcd(a, b)}} \right\rceil

显然可以直接求。

y 最大值

根据 ax + by = c,可得 y = \frac{c - ax}{b}

注意到 y\mathbb{R} 上单调递减,x 取到 x_{\min} 再求出 y_{\max} 即可。

如果 y_{\max} \le 0,就没有正整数解。

y 最小值

考虑 ax_{\max} + by_{\min} = c 这个方程。

由于 ax_{\min} + by_{\max} = c,设 \Delta x = x_{\max} - x_{\min}, \Delta y = y_{\min} - y_{\max}

得:

\begin{cases} \Delta x = t \times \frac{b}{\gcd(a, b)} \\ \Delta y = -t \times \frac{a}{\gcd(a, b)} \end{cases}

根据

\begin{aligned} y_{\min} &= \Delta y + y_{\max} \\ &= y_{\max} - t \times \frac{a}{\gcd(a, b)} \end{aligned}

说明

y_{\min} \equiv y_{\max} \pmod{\frac{a}{\gcd(a, b)}}

由于 y_{\min} 必须是最小正整数,得:

y_{\min} = \begin{cases} y_{\max} \bmod \frac{a}{\gcd(a, b)}, & y_{\max} \bmod \frac{a}{\gcd(a, b)} \ne 0 \\ \frac{a}{\gcd(a, b)}, & y_{\max} \bmod \frac{a}{\gcd(a, b)} = 0 \end{cases}

x 最大值

y_{\max} 一样,把 y_{\min} 代入即可。

解的个数

我们在 (y_{\min}, y_{\max}] 中寻找解。

考虑变换一下通解格式:

\begin{cases} x' = y - t \times \frac{b}{\gcd(a, b)} \\ y' = x + t \times \frac{a}{\gcd(a, b)} \end{cases}

显然,每隔 \frac{a}{\gcd(a, b)} 个就会存在一个解,那么解的个数为 \frac{y_{\max} - y_{\min}}{\frac{a}{\gcd(a, b)}} + 1

对于正无整数解的情况,也是类似的。

::::success[code]

while (T--) {
    int a, b, c;
    cin >> a >> b >> c;
    exgcd(a, b);
    int qwq = __gcd(a, b);
    if (c % qwq) {
        cout << "-1\n";
        continue;
    }
    x = x * c / qwq;
    y = y * c / qwq;
    int X = b / qwq, Y = a / qwq;
    int awa = ceil((1.0 - x) / X);
    x += X * awa;
    y -= Y * awa;
    if (y <= 0) {
        int y_min = y + Y * ceil((1.0 - y) / Y);
        cout << x << ' ' << y_min << '\n';
    }
    else cout << (y - 1) / Y + 1 << ' ' << x << ' ' << (y - 1) % Y + 1 << ' ' << x + (y - 1) / Y * X << ' ' << y << '\n';
}

::::

推荐习题

Lucas 定理

对于 p 是质数,有 \binom{n}{m} = \binom{\left\lfloor \frac{n}{p} \right\rfloor}{\left\lfloor \frac{m}{p} \right\rfloor} \binom{n \bmod p}{m \bmod p} \pmod p

:::::info[证明]{open} ::::info[引理] 对于质数 p 和整数 x,有 (1 + x)^p \equiv 1 + x^p \pmod p

:::info[引理的证明] 由二项式定理可得 (1 + x)^p = \sum_{k = 0}^p \binom{p}{k} x^k。 当 0 < k < p 时,显然 \binom{p}{k} 可以被 p 整除,那么 (1 + x)^p \equiv 1 + x^p \pmod p。 ::: ::::

考虑将 n, m 变一下形式

\begin{cases} n = sp + q, & q = n \bmod p \\ m = tp + r, & r = m \bmod p \end{cases}

考虑将 (1 + x)^n 展开,得到 (1 + x)^n = (1 + x)^{sp + q} = ((1 + x)^p)^s (1 + x)^q

(1 + x)^q 带入引理,可得:

(1 + x)^n \equiv (1 + x^p)^s (1 + x)^q \pmod p

我们来比较两边 x^m 的项数。

左边:(1 + x)^n = \sum_{k = 0}^n \binom{n}{k} x^k,则 x^m 的系数就是 \binom{n}{m}

右边:

某一项指数为 tp + r,注意到 m = tp + r 这一种表示方法是唯一的,所以右边系数只能是 \binom{s}{t} \binom{q}{r}

可得 \binom{n}{m} = \binom{s}{t} \binom{q}{r},转变一下形式就是:

\binom{n}{m} = \binom{\left\lfloor \frac{n}{p} \right\rfloor}{\left\lfloor \frac{m}{p} \right\rfloor} \binom{n \bmod p}{m \bmod p} \pmod p

证毕。 :::::

::::success[code]

int C(int n, int k, int mod) {
    if (k < 0 || k > n) return 0;
    return fact[n] * inv[k] % mod * inv[n - k] % mod;
}
int Lucas(int n, int k, int mod) {
    if (k == 0) return 1;
    return C(n % mod, k % mod, mod) * Lucas(n / mod, k / mod, mod) % mod;
}

::::

例题 P2480

数论大杂烩

给一个整数 n,求:

g^{\sum_{d \mid n} \binom{n}{d}} \bmod 999911659

如果 999911659 \mid g,答案就是 0

注意到模数 999911659 是一个质数,考虑对其使用费马小定理。

::::::info[费马小定理] 若整数 p质数p 与整数 a 互质,则满足:

a^{p - 1} \equiv 1 \pmod p

:::::info[证明] 因为 p 是质数,那么 \varphi(p) = p - 1,这时就转化成了欧拉定理的一种情况,由于满足欧拉定理,费马小定理一定成立。

::::info[欧拉定理] 若正整数 a, n 互质,则:

a^{\varphi(n)} \equiv 1 \pmod n

其中 \varphi(n) 为欧拉函数,表示 1 \sim n 中与 n 互质的数的个数。

:::info[证明] 设 x_1, x_2, \dots, x_{\varphi(n)}1 \sim n 中与 n 互质的数。

这时,我们发现 a \times x_1, a \times x_2, \dots, a \times x_{\varphi(n)} 具有这个性质:每个数 \bmod n 两两不同,且余数与 n 互质。而且,x_1, x_2, \dots, x_{\varphi(n)} 也具有这个性质。

有了这个性质,将 a \times x_1, a \times x_2, \dots, a \times x_{\varphi(n)}\bmod n 后一定是 \varphi(n) 个不同的与 n 互质的数,那么:

x_1 \times x_2 \times \dots \times x_{\varphi(n)} \equiv a \times x_1 \times a \times x_2 \times \dots \times a \times x_{\varphi(n)} \pmod n \\ 1 \equiv a^{\varphi(n)} \pmod n

::: :::: ::::: ::::::

然后,原式变成了:

g^{\sum_{d \mid n} \binom{n}{d} \bmod 999911658} \bmod 999911659

考虑怎么求 \sum_{d \mid n} \binom{n}{d} \bmod 999911658

将 $999911658$ 分解质因数,得到 $999911658 = 2 \times 3 \times 4679 \times 35617$。 设 $\sum_{d \mid n} \binom{n}{d}$ 为 $S$,我们可以列出以下同余方程组: $$ \begin{cases} S \equiv a_1 & \pmod 2 \\ S \equiv a_2 & \pmod 3 \\ S \equiv a_3 & \pmod {4679} \\ S \equiv a_4 & \pmod {35617} \end{cases} $$ 显然,可以使用 CRT 求出方程的解 $S$,对于 $S \bmod p$,用 Lucas 定理求解即可。 ::::success[code] ```cpp int MOD[MAXN] = {0, 2, 3, 4679, 35617}; int C(int n, int k, int mod) { if (k < 0 || k > n) return 0; return fact[n] * inv[k] % mod * inv[n - k] % mod; } int Lucas(int n, int k, int mod) { if (k == 0) return 1; return C(n % mod, k % mod, mod) * Lucas(n / mod, k / mod, mod) % mod; } const int mod1 = 999911659; const int mod2 = 999911658; int CRT() { int ans = 0; for (int i = 1; i <= tot_mod; i++) { int qwq = mod2 / MOD[i]; int awa = qpow(qwq, MOD[i] - 2, MOD[i]); ans = (ans + qwq * a[i] % mod2 * awa % mod2) % mod2; } return (ans + mod2) % mod2; } void get(int x) { init(MOD[x]); for (int i = 1; i <= tot_d; i++) a[x] = (a[x] + Lucas(n, d[i], MOD[x])) % MOD[x]; } ``` :::: ## 扩展 Lucas 定理 / exLucas ~~说实话,这个定理跟 Lucas 定理没有半毛钱关系~~\ ~~唉,我讲了 exLucas 为什么还要讲 Lucas 定理~~ > 求 $\binom{n}{m} \bmod p$,$p$ 不一定是质数。 设 $p = p_1^{k_1} \times p_2^{k_2} \times \dots$。 于是,可以用 CRT 求出以下方程的解,解就是 $\binom{n}{m}$: $$ \begin{cases} \binom{n}{m} \equiv a_1 & \pmod {p_1^{k_1}} \\ \binom{n}{m} \equiv a_2 & \pmod {p_2^{k_2}} \\ \dots \end{cases} $$ 现在考虑怎么求出 $\binom{n}{m} \bmod p^k$。 $$ \binom{n}{m} \bmod p^k = \frac{n!}{m!(n - m)!} \bmod p^k $$ 把式子转换成 $$ \frac{\frac{n!}{p^x}}{\frac{m!}{p^y} \frac{(n - m)!}{p^z}} \bmod p^{x - y - z} $$ $x, y, z$ 为 $n!, m!, (n - m)!$ 包含多少 $p$ 的因子。 考虑怎么求出 $\frac{n!}{p^x}$,其他照猫画虎。 $$ \begin{aligned} n! &= (1 \times 2 \times 3 \times \cdots)(p \times 2p \times 3p \times \cdots) \\ &= p^{\left\lfloor \frac{n}{p} \right\rfloor} (1 \times 2 \times \cdots) (1 \times 2 \times 3 \times \cdots) \\ &= p^{\left\lfloor \frac{n}{p} \right\rfloor} \times \left(\left\lfloor \frac{n}{p} \right\rfloor\right)! \times \prod_{\substack{i=1 \\ i \not\equiv 0 \pmod p}}^n i \end{aligned} $$ 注意到这个 $\pmod p$ 显然是有循环节的。 于是: $$ n! = p^{\left\lfloor \frac{n}{p} \right\rfloor} \times \left(\left\lfloor \frac{n}{p} \right\rfloor\right)! \times \left(\prod_{i = 1, i \not\equiv 0 \pmod p}^{p^k} i\right)^{\left\lfloor \frac{n}{p^k} \right\rfloor} \left(\prod_{i = p^k \left\lfloor \frac{n}{p^k} \right\rfloor, i \not\equiv 0 \pmod p}^n i\right) $$ 第三部分是循环节的乘积,最后是循环后面剩下的乘积。 注意到 $p^{\left\lfloor \frac{n}{p} \right\rfloor}$ 是要除掉的,而 $\left(\left\lfloor \frac{n}{p} \right\rfloor\right)!$ 可能还包含 $p$。 定义 $f(n) = \frac{n!}{p^k}$。 $$ f(n) = f\left(\left\lfloor \frac{n}{p} \right\rfloor\right) \left(\prod_{i=1, i\not\equiv 0 \pmod p}^{p^k} i\right)^{\left\lfloor \frac{n}{p^k} \right\rfloor} \left(\prod_{i=p^k \left\lfloor \frac{n}{p^k} \right\rfloor, i\not\equiv 0 \pmod p}^n i\right) $$ 这样就好了。时间复杂度是 $O(\log_p n)$。 边界 $f(0) = 1$。 看看原式 $$ \frac{ \frac{n!}{p^x} }{ \frac{m!}{p^y} \frac{(n-m)!}{p^z} } \cdot p^{\,x-y-z} \bmod p^k \\ = \frac {f(n)}{f(m)f(n-m)} p^{x-y-z} \bmod p^k $$ 考虑 $p^{x - y - z}$ 怎么求。 设 $g(n) = x$(上述 $p^{x - y - z}$ 中的 $x$)。 我们查看原来的式子,可以发现,$n!$ 中显然有 $ \left\lfloor \frac{n}{p} \right\rfloor$ 个 $p$,然后还剩 $\left\lfloor \frac{n}{p} \right\rfloor !$ 的 $p$ 还没有求出,可以递归到 $g\left(\left\lfloor \frac{n}{p} \right\rfloor\right)$。 所以我们可以得到以下递推式 $$ g(n) = \left\lfloor \frac{n}{p} \right\rfloor + g\left(\left\lfloor \frac{n}{p} \right\rfloor\right) $$ 时间复杂度是 $O(\log_p n)$。 边界 $g(n) = 0 \ (n < p)$。 所以答案就是 $$ \frac {f(n)}{f(m)f(n-m)} p^{g(n)-g(m)-g(n-m)} \bmod p^k $$ ::::success[code] ```cpp int exgcd(int a, int b, int &x, int &y) { if (!b) { x = 1, y = 0; return a; } int d = exgcd(b, a % b, y, x); y -= a / b * x; return d; } int inv(int a, int p) { int x, y; exgcd(a, p, x, y); return (x % p + p) % p; } int qpow(int a, int b, int p) { int ans = 1; a %= p; while (b) { if (b & 1) ans = ans * a % p; a = a * a % p; b >>= 1; } return ans; } int f(int n, int p, int PK) { if (n == 0) return 1; int res = 1, ans = 1; for (int i = 1; i <= PK; i++) if (i % p) res = res * i % PK; res = qpow(res, n / PK, PK); for (int i = PK * (n / PK); i <= n; i++) if (i % p) ans = ans * (i % PK) % PK; return f(n / p, p, PK) * res % PK * ans % PK; } int g(int n, int p) { if (n < p) return 0; return n / p + g(n / p, p); } int C_mod(int n, int m, int p, int PK) { int a = f(n, p, PK), b = inv(f(m, p, PK), PK), c = inv(f(n - m, p, PK), PK); int qwq = qpow(p, g(n, p) - g(m, p) - g(n - m, p), PK); return a * b % PK * c % PK * qwq % PK; } const int MAXN = 1e5 + 10; int A[MAXN], B[MAXN]; void exLucas(int n, int m, int p) { int now = p; int tot = 0; for (int i = 2; i * i <= p; i++) { if (now % i == 0) { int PK = 1; while (now % i == 0) { now /= i; PK *= i; } A[++tot] = PK; B[tot] = C_mod(n, m, i, PK); } } if (now > 1) { A[++tot] = now; B[tot] = C_mod(n, m, now, now); } int ans = 0; for (int i = 1; i <= tot; i++) { int qwq = p / A[i], awa = inv(qwq, A[i]); ans = (ans + B[i] * qwq % p * awa % p) % p; } cout << ans; } ``` :::: ## 扩展中国剩余定理 / exCRT CRT 详见浅谈数论。 ~~和 exLucas 一样,exCRT 和 CRT 也没有半毛钱关系~~ > 求解以下同余方程组: > $$ > \begin{cases} > x \equiv a_1 & \pmod {b_1} \\ > x \equiv a_2 & \pmod {b_2} \\ > \dots \\ > x \equiv a_n & \pmod {b_n} > \end{cases} > $$ > 其中 $a$ 是正整数,$b$ 是非负整数。 考虑从头开始,一个一个合并这些同余方程。 设当前同余方程为 $x \equiv B \pmod A$,需要和方程 $x \equiv b \pmod a$ 合并。 显然,可以得到: $$ x + AX = B \ (X \in \mathbb{Z}) \\ x + aY = b \ (Y \in \mathbb{Z}) $$ 整理两个方程: $$ B - AX = b - aY \\ AX - aY = B - b $$ 可以使用 exgcd 求出一组特解 $x_0, y_0$,再求解一组通解: $$ X = x_0 + \frac{a}{\gcd(a, A)} \times k \\ Y = y_0 - \frac{A}{\gcd(a, A)} \times k $$ 将通解带入: $$ \begin{aligned} x &= B - AX \\ &= B - A\left(x_0 + \frac{a}{\gcd(a, A)} \times k\right) \\ &= B - Ax_0 - \frac{A \times a}{\gcd(a, A)} \times k \\ &= B - Ax_0 - \operatorname{lcm}(a, A) \times k \end{aligned} $$ 显然,最后是一个同余方程: $$ x \equiv B - Ax_0 \pmod {\operatorname{lcm}(a, A)} $$ 依次合并每个方程即可,注意初始时 $A = 1, B = 0$。 ::::success[code] ```cpp #include <bits/stdc++.h> #define int long long using namespace std; int n, A, B; int mul(int a, int b, int mod) { int ans = 0; while (b) { if (b & 1) ans = (ans + a) % mod; a = (a + a) % mod; b >>= 1; } return ans; } int x, y; void exgcd(int a, int b) { if (b == 0) { x = 1, y = 0; return; } exgcd(b, a % b); int Gcd = x; x = y; y = Gcd - a / b * y; } signed main() { ios::sync_with_stdio(0); cin.tie(0), cout.tie(0); cin >> n; A = 1, B = 0; for (int i = 1; i <= n; i++) { int a, b; cin >> a >> b; exgcd(A, a); int d = __gcd(A, a); int k = a / d; int qwq = (((B - b) / d) % k + k) % k; x = (x % k + k) % k; x = mul(x, qwq, k); B = B - A * x; A = A / d * a; B %= A; if (B < 0) B += A; } cout << B % A; return 0; } ``` :::: ### 例题 P4774 > 有 $n$ 条巨龙,你要按顺序击杀。 > 初始有 $m$ 把剑,攻击力已知。 > > 面对第 $i$ 条巨龙时: > - 从当前剑中选出攻击力不超过 $a_i$ 的最大的一把;若不存在,则选攻击力最小的。 > - 用这把剑(攻击力记为 $ATK_i$)攻击 $x$ 次,造成 $x \cdot ATK_i$ 伤害。 > - 巨龙初始生命值为 $a_i$,每次恢复 $p_i$ 生命值。 > 要求经过若干次恢复后,生命值恰好为 $0$。 > > 你需要找到一个最小的整数 $x$,使得对所有 $i$ 都成立。 > 若不存在这样的 $x$,输出 $-1$。 显然,每一条龙都有一把对应的剑,我们使用 multiset 预处理出每一条龙对应的剑的攻击力 $b_i$。 于是,题意转化为求解以下同余方程组的最小非负整数解: $$ \begin{cases} b_1x \equiv a_1 & \pmod {p_1} \\ b_2x \equiv a_2 & \pmod {p_2} \\ \dots \\ b_nx \equiv a_n & \pmod {p_n} \end{cases} $$ 考虑使用 exCRT 求解,和模板相比,换汤不换药。 按照 exCRT 的推导方式,设当前在合并第 $i$ 个方程,令 $g_1 = \gcd(b_i, p_i)$,$b = \frac{a_i}{g_1} \cdot \left(\frac{b_i}{g_1}\right)^{-1} \bmod {\frac{p_i}{g_1}}$,$g = \gcd(A, a)$,则: $$ x \equiv B - A \cdot \left( x_0 \cdot \frac{B - b}{g} \right) \pmod{\operatorname{lcm}(A, a)} $$ 注意判断无解。 ## 大步小步算法 / BSGS > 给定三个整数 $a, b, p$,保证 $a \perp p$,求一个最小的非负整数 $x$,使得 $a^x \equiv b \pmod p$。 定义 $x = im - j, m = \left\lceil \sqrt p \right\rceil$,其中 $1 \le i \le m, 0 \le j \le m$,显然,对于 $x \in [0, p]$ 都可以用 $im - j$ 表示出来。 将 $im - j$ 代入,得: $$ a^{im - j} \equiv b \pmod p \\ (a^m)^i \equiv b \cdot a^j \pmod p $$ 我们枚举 $j$,令 $s = b \cdot a^j \bmod p, t = j$,放入一个 `unordered_map`,如果 $s$ 重复,使用更大的 $t$ 替换小的。 枚举 $i$,查找哈希表中是否有 $s = (a^m)^i$,如果有,说明我们找出了一组解 $x = im - j$。 由于 $i, j \le \left\lceil \sqrt p \right\rceil$,显然时间复杂度是 $O(\sqrt p)$。 ::::success[code] ```cpp // b, n, p 为题目中的 b, n, p int BSGS() { int qwq = sqrt(p); if (qwq * qwq != p) qwq++; mp[n] = 0; for (int i = 1; i <= qwq; i++) { n = (n * b) % p; mp[n] = i; } int Pow = 1; for (int i = 1; i <= qwq; i++) Pow = (Pow * b) % p; int POW = 1; for (int i = 1; i <= qwq; i++) { POW = (POW * Pow) % p; if (mp.count(POW)) return i * qwq - mp[POW]; } return -1; } ``` :::: ## 扩展大步小步算法 / exBSGS ~~不像前面几个,exBSGS 和 BSGS 终于有关系了~~ > 给定三个整数 $a, b, p$,求一个最小的非负整数 $x$,使得 $a^x \equiv b \pmod p$。 和 BSGS 相比,$a$ 和 $p$ 不一定互质,怎么办? 我们注意到 $a^x \equiv b \pmod p$ 等价于 $$ a^x + kp = b \ (k \in \mathbb{Z}) $$ 我们令 $d = \gcd(a, p)$,根据裴蜀定理,只有 $d \mid b$ 才有解,$d \nmid b$ 无解。 如果 $d \mid b$,我们将等式两边同时除以 $d$。 $$ \begin{aligned} & a^x + kp = b \\ & \Leftrightarrow \frac{a}{d} a^{x - 1} + k \frac{p}{d} = \frac{b}{d} \\ & \Leftrightarrow \frac{a}{d} a^{x - 1} \equiv \frac{b}{d} \pmod {\frac{p}{d}} \\ & \Leftrightarrow a^{x - 1} \equiv \frac{b}{d} \times \left(\frac{a}{d}\right)^{-1} \pmod {\frac{p}{d}} \end{aligned} $$ 我们成功实现了降次。如果 $a$ 和 $\frac{p}{d}$ 已经互质了,可以直接 BSGS,如果没有,继续降次即可。 ::::success[code] ```cpp int BSGS() { int qwq = sqrt(p); if (qwq * qwq != p) qwq++; mp[n] = 0; for (int i = 1; i <= qwq; i++) { n = (n * b) % p; mp[n] = i; } int Pow = 1; for (int i = 1; i <= qwq; i++) Pow = (Pow * b) % p; int POW = 1; for (int i = 1; i <= qwq; i++) { POW = (POW * Pow) % p; if (mp.count(POW)) return i * qwq - mp[POW]; } return -1; } int solve() { if (b == 1 || p == 1) return 0; int sum = 0, real_a = 1; while (114514) { int d = __gcd(a, p); if (d == 1) break; if (b % d) return -1; sum++; b /= d, p /= d; real_a = real_a * (a / d) % p; if (real_a == b) return sum; } int ans = BSGS(real_a); if (ans == -1) return -1; else return ans + sum; } ``` ::::