浅谈同余3(扩展中国剩余定理,扩展BSGS)


距离上一篇已经四个月了,我来填坑了

上一篇:2(BSGS)

(https://www.cnblogs.com/xyy-yyds/p/17418472.html)

0x50 扩展BSGS O(n)


【模板】扩展 BSGS/exBSGS

 题目背景

题目来源:SPOJ3105 Mod

题目描述

给定 a,p,b,求满足 axb(modp) 的最小自然数 x

输入格式

每个测试文件中包含若干组测试数据,保证 p5×106

每组数据中,每行包含 3 个正整数 a,p,b

a=p=b=0 时,表示测试数据读入完全。

输出格式

对于每组数据,输出一行。

如果无解,输出 No Solution,否则输出最小自然数解。

样例输入 #1

5 58 33 2 4 3 0 0 0

样例输出 #1

9 No Solution

提示

对于 100% 的数据,1a,p,b109a=p=b=0

2021/5/14 加强 by [SSerxhs](https://www.luogu.com.cn/user/29826)。
2021/7/1 新添加[一组 Hack 数据](https://www.luogu.com.cn/discuss/391666)。

本题a,p不互质,所以我们要把a,p变得互质
即除去它们的公因数
当然,根据Be´zout定理当gcd(a,p)b时,方程无解
即求axdbd(modpd) 的答案再加上除的次数就是答案
也可以写成axcntacntdbd(modpd)
acntd要当参数传进去,即代码里的add

注意,本题卡常,此代码要开C++20+O2才能过
我才不会告诉你这道题我卡了一屏TLE呢
Code:

复制代码
#include <iostream> #include <unordered_map> #include <cmath> using namespace std; typedef long long LL; inline int read() { register int s = 0, f = 1; register char c = getchar(); for ( ; !isdigit(c); c = getchar()) if (c == '-') f = -1; for ( ; isdigit(c); c = getchar()) s = (s << 1) + (s << 3) + (c ^ 48); return s * f; } inline int qmi(int a, int b, int p) { register int res = 1 % p; while (b) { if (b & 1) res = (LL)res * a % p; b >>= 1; a = (LL)a * a % p; } return res; } unordered_map<int, int> Hash; inline int bsgs(int a, int b, int p, int add) //普通BSGS { Hash.clear(); register int t = (int)sqrt(p) + 1; register int tmp = 1; for (register int j = 0; j < t; j ++ ) { int val = (LL)b * tmp % p; Hash[val] = j; tmp = (LL)tmp * a % p; //递推减少快速幂的log } a = qmi(a, t, p); tmp = 1; for (register int i = 0; i <= t; i ++ ) { int val = (LL)add * tmp % p; //同上 int j = Hash.find(val) == Hash.end() ? -1 : Hash[val]; if (j >= 0 && i * t - j >= 0) return i * t - j; tmp = (LL)tmp * a % p; } return -1; } inline int gcd(int a, int b) { return b ? gcd(b, a % b) : a; } inline int exbsgs(int a, int b, int p) { a %= p, b %= p; if (b == 1 || p == 1) return 0; register int cnt = 0; register int d, add = 1; while ((d = gcd(a, p)) ^ 1) //消除因子 { if (b % d) return -1; //bezout判无解 cnt ++, b /= d, p /= d; add = (LL)add * a / d % p; //a^cnt / d if (add == b) return cnt; } int res = bsgs(a, b, p, add); if (res == -1) return -1; return res + cnt; //别忘记+cnt } int main() { int a, b, p, res; a = read(), p = read(), b = read(); while (a || b || p) { res = exbsgs(a, b, p); if (res == -1) puts("No Solution"); else printf("%d\n", res); a = read(), p = read(), b = read(); } return 0; }
复制代码

 

0x60 扩展中国剩余定理
求方程组
{xa1(modm1) xa2(modm2) xa3(modm3) ... xan(modmn) 
的最小整数解,其中m1,m2,...,mn不两两互质

设之前1i1个方程的解为x
i1方程的通解可以表示为x+iM, 其中M=lcmm1,m2,...,mi1
i个方程合并,可得方程
x+kMai(modmi)
这就是同余方程,可用exgcd求解,然后x加上kM就是前i个方程的解
要求最小解,所以要对M取模

具体可以去看进阶指南和墨染空巨佬的这个(https://www.acwing.com/solution/content/3539/)

以洛谷模板P4777 【模板】扩展中国剩余定理(EXCRT)为例
由于数据范围很大,要炸long long, 所以要用int128
Code:

复制代码
#include <iostream> #include <cstring> using namespace std; typedef __int128 LL; int n; LL exgcd(LL a, LL b, LL &x, LL &y) //扩展欧几里得 { if (!b) { x = 1, y = 0; return a; } LL d = exgcd(b, a % b, y, x); y -= a / b * x; return d; } LL gcd(LL a, LL b) { return b ? gcd(b, a % b) : a; } LL lcm(LL a, LL b) { return a / gcd(a, b) * b; } int main() { scanf("%d", &n); LL a1, m1, a2, m2, x, y; // x mod m1 = a1 long long M, A; scanf("%lld%lld", &M, &A); m1 = M, a1 = A; for (int i = 2; i <= n; i ++ ) { scanf("%lld%lld", &M, &A); m2 = M, a2 = A; LL d = exgcd(m1, m2, x, y); if ((a2 - a1) % d) //bezout判无解 { puts("-1"); return 0; } x = x * (a2 - a1) / d % (m2 / d); if (x < 0) x += m2 / d; LL mod = lcm(m1, m2); a1 = (m1 * x + a1) % mod; if (a1 < 0) a1 += mod; m1 = mod; } printf("%lld\n", (long long)(a1 % m1)); return 0; }
复制代码

 


__EOF__

本文作者xyy's blog
本文链接https://www.cnblogs.com/xyy-yyds/p/17418478.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   xuyiyang  阅读(23)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 一文读懂知识蒸馏
· 终于写完轮子一部分:tcp代理 了,记录一下
点击右上角即可分享
微信分享提示