[BZOJ2242][SDOI2011]计算器
一道非常经典的同余方程入门题
题意
- 计算pow(a, b)
- 线性同余方程
- 高次同余方程
分析
pow(a, b)#
我们可以使用快速幂完成这项操作
qword qpow(qword a, qword b, qword p) {
a %= p; qword res = 1 % p; for (; b; b >>= 1, a = a * a % p) if (b & 1) res = res * a % p; return res;
}
线性同余方程#
求解线性同余方程我们一般使用拓展欧几里得算法。
这里稍微证明一下这种算法的正确性并描述一下这种算法
Bezout(贝祖)定理:对于任意整数 ,存在一对整数 ,满足
证明:
当 时, 显然有一对整数 使得
若 ,则 。假设存在一对整数 满足 .
因为
所以令 就得到了
应用数学归纳法,可知定理成立。
Bezout定理是按照欧几里得算法的流程进行证明的,所以这种能同时计算 的算法叫做扩展欧几里得算法
例如在本题中,由于 是给定的参数,我们不妨将其设为
原式变成了 我们令 就有 。(这里的 是同余方程中设出的另一个解)
易知,当 时有解。我们最终只需要解出 的值,并且扩大 即可。
qword exgcd(qword a, qword b, qword &x, qword &y) {
if (b == 0) { x = 1, y = 0; return a; }
qword d = exgcd(b, a % b, x, y);
qword z = x; x = y; y = z - y * (a / b);
return d;
}
高次同余方程#
求解高次同余方程我们一般使用Baby Step Gaint Step算法
求解形式:, 要求 互质
算法复杂度:$O(\sqrt{p})
因为 互质,所以可以在模 意义下执行关于 的乘、除运算。
设 其中 ,则方程变为 ,即
对于所有的 ,把 插入一个hash表中
枚举 的所有可能取值,计算出 在hash表中是否存在对应的 ,更新答案即可。
qword baby_step_gaint_step(qword a, qword b, qword p) {
mp.clear();
b %= p;
qword t = (qword)sqrt(p) + 1;
for (int j = 0; j < t; ++ j) {
qword val = (qword)b * qpow(a, j, p) % p;
mp[val] = j;
}
a = qpow(a, t, p);
if (a == 0) return b == 0 ? 1 : -1;
for (int i = 0; i <= t; ++ i) {
qword val = qpow(a, i, p);
qword j = mp.find(val) == mp.end() ? -1 : mp[val];
if (j >= 0 && i * t - j >= 0) return i * t - j;
}
return -1;
}
完整代码
#include <bits/stdc++.h>
using namespace std;
typedef long long qword;
qword qpow(qword a, qword b, qword p) {
a %= p; qword res = 1 % p; for (; b; b >>= 1, a = a * a % p) if (b & 1) res = res * a % p; return res;
}
qword exgcd(qword a, qword b, qword &x, qword &y) {
if (b == 0) { x = 1, y = 0; return a; }
qword d = exgcd(b, a % b, x, y);
qword z = x; x = y; y = z - y * (a / b);
return d;
}
map<qword, qword> mp;
qword baby_step_gaint_step(qword a, qword b, qword p) {
mp.clear();
b %= p;
qword t = (qword)sqrt(p) + 1;
for (int j = 0; j < t; ++ j) {
qword val = (qword)b * qpow(a, j, p) % p;
mp[val] = j;
}
a = qpow(a, t, p);
if (a == 0) return b == 0 ? 1 : -1;
for (int i = 0; i <= t; ++ i) {
qword val = qpow(a, i, p);
qword j = mp.find(val) == mp.end() ? -1 : mp[val];
if (j >= 0 && i * t - j >= 0) return i * t - j;
}
return -1;
}
qword t, op, x, y;
int main() {
cin >> t >> op;
for (int i = 1, a, b, p; i <= t; ++ i) {
cin >> a >> b >> p;
if (op == 1) cout << qpow(a, b, p) << endl;
else if (op == 2) {
qword d = exgcd(a, p, x, y);
if (b % d) cout << "Orz, I cannot find x!" << endl;
else {
x *= b / d;
cout << (x % p + p) % p << endl;
}
} else if (op == 3){
x = baby_step_gaint_step(a, b, p);
if (x == -1) cout << "Orz, I cannot find x!" << endl;
else cout << (x % p + p) % p << endl;
}
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具