1 条题解
-
0
自动搬运
来自洛谷,原作者为

小粉兔
Always continue; Never break;搬运于
2025-08-24 22:09:48,当前版本为作者最后更新于2019-05-05 18:53:10,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
在博客园食用更佳:https://www.cnblogs.com/PinkRabbit/p/LuoguP5345.html。
很荣幸为 X Round 1 贡献了自己的一题。
题意简述:
给定 组 ()。
求关于 的方程组 $\left\{\begin{matrix}k_1^x\equiv r_1\pmod{g_1}\\k_2^x\equiv r_2\pmod{g_2}\\\vdots\\k_n^x\equiv r_n\pmod{g_n}\end{matrix}\right.$(定义 )在 内的最小整数解,或判断在这个范围内无解。
题解:
分为两个部分解决。
第一部分:分别求出每个方程的解。
首先观察 的规律,以 , 为例:
定义 ,则把每个在 之间的整数看作一个节点后,形成了一个基环内向森林。
从 开始走唯一的出边,必然形成一个 形结构,让我们模拟这个过程:

可以看到,出现了长度为 的循环。
-
如果 ,则 ,且 。
-
如果 ,则 。
-
如果 ,则无解。
以上就是每个方程的 种解,无限解,唯一解或无解。
那么,问题就是,如何区分这 种解,以及如何求出解。
首先,需要将 形的“尾巴”和循环分开考虑,不难证明“尾巴”的长度不超过 。
而且可以发现,若在“尾巴”上找到了解,那么就只有一组解。所以需要判断一个值是否在尾巴上。
其实这是很简单的,如果 ,那么 就在尾巴上,反之就在循环内。
据此,我们区分了在“尾巴”上的解,也就是只有一组解的情况,接下来考虑不在“尾巴”上的情况。
因为解不在“尾巴”上,则如果解不在循环内就是无解了,所以首先判断解是否在循环内。
假设第一个在循环上的数为 ,尾巴长度为 ,则有 。
令 ,则原方程 可化为 且 ,这是因为我们假定解一定在循环内。
可以进一步化为 $k^{x-o}\equiv\frac{r}{d}\left(\frac{c}{d}\right)^{-1}\pmod{\frac{g}{d}}$,若 不是 的倍数则无解。
这是因为循环内的值均是 的倍数,可以让方程同除 ,又因为 ,可以取逆元。
令 $a=k,b=\frac{r}{d}\left(\frac{c}{d}\right)^{-1},m=\frac{g}{d}$,则有 。
使用 BSGS 求出 的最小自然数解 后,有原方程的最小自然数解为 。
再使用 BSGS 求出 的最小正整数解 ,即 对 的阶。
则原方程的解为 且 。
至此,第一部分解决。
第二部分:合并每个方程的解。
首先,若前面的方程出现了无解的情况,则方程组也无解。
若前面的方程出现了只有唯一解的情况,只需要检查此唯一解是否满足所有方程即可。
接下来讨论以上每个方程的解均形如 且 的形式。
分开考虑前半部分和后半部分,对于前半部分,显然是 ExCRT 的形式。
对于后半部分,可以化为 ,令 ,放到最后考虑。
考虑使用 ExCRT 解决前半部分,令前 个方程组的解为 ,有 。
但是这里出现一个问题,那就是 可能很大,但是这里其实没必要使用高精度,要怎么处理这种情况呢?
因为以上方程的解 中, 均小于 ,所以不需要担心这部分。
考虑这样一种情况:,而再合并进 可能会让 超出
long long能够表示的范围。这时其实不需要再合并了,只需要判断 是否满足第 个方程即可。因为若不满足,合并后 必然会至少增加一倍的 ,这就超出了 的范围,从而可以直接输出无解。
最后,当方程成功合并完之后, 可能不是真实值,但是当 时必然是真实值。
此时可以再考虑 ,真实的解应该为 $x=\left\lceil\frac{x_0-P_n}{Q_n}\right\rceil\cdot Q_n+P_n$。
若 ,则必然要满足 ,否则无解。
据此写出代码,复杂度 $\mathcal{O}\left(\sum_{i=1}^{n}\left(\sqrt{g_i}+\log g_i\right)\right)$:
#include <cstdio> #include <algorithm> #include <cstring> #include <cmath> #define Fail return puts("Impossible"), 0 #define mp std::make_pair typedef long long LL; typedef std::pair<int, int> pii; const int MG = 10000005, MS = 3175; const int U = 1e9; int gcd(int a, int b) { return b ? gcd(b, a % b) : a; } template<typename T> T exgcd(T a, T b, T &x, T &y) { if (!b) return x = 1, y = 0, a; int d = exgcd(b, a % b, y, x); return y -= a / b * x, d; } int buk[MG], stk[MS]; inline int BSGS(int a, int b, int m) { int S = sqrt(m - 1) + 1; int A = 1, f = -1, t = 0; for (int i = 0; i < S; ++i) { buk[stk[++t] = (LL)b * A % m] = i; A = (LL)A * a % m; } int C = 1; for (int i = 1; !~f && i <= S; ++i) if (~buk[C = (LL)C * A % m]) f = i * S - buk[C]; while (t) buk[stk[t--]] = -1; return f; } inline pii ExBSGS(int a, int b, int m) { int o = 0, A = 1 % m, d = 1, nd, x, y; while (1) { if (d == (nd = gcd((LL)A * a % m, m))) break; if (A == b) return mp(o, -1); ++o, A = (LL)A * a % m, d = nd; } if (b % d) return mp(-1, -1); m /= d, b /= d, A /= d; exgcd(A, m, x, y); b = (LL)b * (x + m) % m; x = BSGS(a, b, m); if (!~x) return mp(-1, -1); y = BSGS(a, 1 % m, m); return mp(x % y + o, y); } inline bool Combine(LL &a1, LL &m1, LL a2, LL m2) { LL k1, k2, g = exgcd(m1, m2, k1, k2); if ((a2 - a1) % g) return 0; a1 += (k1 * ((a2 - a1) / g) % m2 + m2) * m1 % (m1 / g * m2); return a1 %= m1 *= m2 / g, 1; } const int MN = 1005; int N; int k[MN], r[MN], g[MN]; int x[MN], q[MN], X, MaxX; int main() { memset(buk, -1, sizeof buk), X = -1; scanf("%d", &N); for (int i = 1; i <= N; ++i) { scanf("%d%d%d", &k[i], &g[i], &r[i]); pii ans = ExBSGS(k[i] % g[i], r[i] % g[i], g[i]); x[i] = ans.first, q[i] = ans.second; if (!~x[i]) Fail; if (!~q[i]) X = x[i]; MaxX = std::max(MaxX, x[i]); } if (~X) { for (int i = 1; i <= N; ++i) { if (~q[i] && (X < x[i] || (X - x[i]) % q[i])) Fail; if (!~q[i] && X != x[i]) Fail; } return printf("%d\n", X), 0; } LL P = 0, Q = 1; for (int i = 1; i <= N; ++i) { if (Q > U && (P - x[i]) % q[i]) Fail; if (Q <= U && !Combine(P, Q, x[i] % q[i], q[i])) Fail; } if (P < MaxX) P += ((MaxX - P - 1) / Q + 1) * Q; if (P > U) Fail; printf("%lld\n", P); return 0; } -
- 1
信息
- ID
- 4312
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 6
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者