(ex)BSGS/(扩展)大步小步算法 学习笔记

这篇具有很好参考价值的文章主要介绍了(ex)BSGS/(扩展)大步小步算法 学习笔记。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

(ex)BSGS/(扩展)大步小步算法 学习笔记

在即将暂时退役之际杀掉了 P4195 的毒瘤模板题,于是来写篇学习笔记。

谨此为我初中三年摆烂的OI生涯画上一个句号。(距离中考还有20天!)

BSGS

link

\(a^x\equiv b\pmod p\)非负整数解,其中\(a, p\)互质。

算法思路

我们不妨令 \(t=\lceil{\sqrt{p}\rceil}\)\(j\lt t\)\(i\leq t\)

原式转化为 \(a^{it-j} \equiv b \pmod p\)

\(\left(a^t\right)^i\equiv b\cdot a^j \pmod p\)

于是我们可以这么在 \(\Theta\left(\sqrt{n}\right)\)(不考虑 hash 表)内求出解:

  • 枚举 \(\forall j \in [0, t)\),求出所有的 \(b\cdot a^j \bmod p\),用 hash 表记录下来

  • 枚举 \(\forall i \in [0, t]\),求出所有的 \(\left(a^t\right)^i \bmod p\),在 hash 表中查找是否有对应的 \(j\)

需要注意的是,当 \(a^t \bmod p=0\) 时,若 \(b=0\),则解为 \(x=1\)
否则无解。

正确性讨论

由 Euler 定理,我们有 \(a^{\varphi\left(p\right)}\equiv 1\pmod p\),其中 \(\varphi\left(x\right)\) 为 Euler 函数。

也就是说,\(\bmod p\) 意义下,\(a^x\bmod p\)\(x=n\cdot\varphi\left(p\right)\)\(n\) 遍历非负整数)后循环。

我们知道对于任意整数 \(p \gt 1\)\(p>\varphi\left(p\right)\),而我们取的 \(it-j\) 可以遍历 \([0, x]\),因此能够取到 \(a^p \bmod p\) 的一切情况,故一定不会漏解。

代码实现

#include <bits/stdc++.h>

using namespace std;

#define int long long

int qpow(int a, int n, int p) {
    a%=p;
    int res=1;
    while (n) {
        if (n&1) res=res*a%p;
        a=a*a%p;
        n>>=1;
    }
    return res;
}

signed bsgs(int a, int b, int p) {
    b%=p;
    int t=sqrt(p)+1;
    unordered_map<int, int> hash; hash.clear();
    for (int j=0; j<t; ++j) {
        int power=b*qpow(a,j,p)%p;
        hash[power]=j;
    }
    a=qpow(a,t,p);
    if (a==0) return b==0?1:-1;
    for (int i=0; i<=t; ++i) {
        int val=qpow(a,i,p);
        int j=hash.find(val)==hash.end()?-1:hash[val];
        if (j>=0 && i*t-j>=0) return i*t-j;
    }
    return -1;
}

signed main() {
    int a, b, p;
    cin>>p>>a>>b;
    int res=bsgs(a,b,p);
    if (res==-1) puts("no solution");
    else cout<<res<<endl;
    return 0;
}

这份代码是可以通过 P3846 的。我们可以考虑对它进行优化。

我们发现枚举 \(a^j\)\(\left(a^t\right)^i\) 的时候,\(i, j\) 是递增的,于是我们可以直接用一个变量来维护 $a^j $和 \(\left(a^t\right)^i\)

另外,用 unordered_map 来实现 hash 表似乎会快一些。

inline int bsgs(int a, int b, int p) {
    int t=(int)(sqrt(p))+1;
    unordered_map<int,int> h; h.clear();
    int powa=1;
    for (reg int j=0; j<t; ++j) {
        int val=powa*b%p;
        h[val]=j;
        powa=powa*a%p;
    }
    a=qpow(a,t,p);
    powa=1;
    for (reg int i=0; i<=t; ++i) {
        int val=powa%p;
        int j=h.find(val)==h.end()?-1:h[val];
        if (j>=0 && i*t-j>=0) return i*t-j;
        powa=powa*a%p;
    }
    return -1;
}

为exBSGS进行修改

我们现在来考虑 \(ka^x\equiv b\pmod p\)\(a, p\) 互质,\(k\) 为正整数)的式子,我们可以同样地将它们变形为

\[k\cdot \left(a^t\right)^i\equiv b\cdot a^j \pmod p \]

于是稍微修改一下上述代码就可以了。

inline int bsgs(int a, int b, int k, int p) {
    int t=(int)(sqrt(p))+1;
    unordered_map<int,int> h; h.clear();
    int powa=1;
    for (reg int j=0; j<t; ++j) {
        int val=powa*b%p;
        h[val]=j;
        powa=powa*a%p;
    }
    a=qpow(a,t,p);
    powa=1;
    for (reg int i=0; i<=t; ++i) {
        int val=k*powa%p;
        int j=h.find(val)==h.end()?-1:h[val];
        if (j>=0 && i*t-j>=0) return i*t-j;
        powa=powa*a%p;
    }
    return -1;
}

exBSGS

link

\(a^x\equiv b\pmod p\)非负整数解,其中 \(a, p\) 未必互质

算法思路

考虑利用同余式的约化性质,转换成朴素的 BSGS 来求解。

我们有如下同余式的约化性质:
\(a\equiv b\pmod p\)\(d\mid a,d \mid b\),则

\[\frac{a}{d}\equiv\frac{b}{d}\pmod {\frac{p}{\gcd(d,p)}} \]

我们回到 \(a^x\equiv b\pmod p\),令 \(d_1=\gcd(a,p)\)
\(d_1 \mid b\),我们可以得到

\[\frac{a}{d_1}\cdot a^{x-1}\equiv \frac{b}{d_1} \pmod {\frac{p}{d_1}} \]

(若 \(d_1 \nmid b\),立刻得出无解)
\(a,\frac{p}{d_1}\) 仍不互质,我们可以继续令 \(d_2=\gcd(a,\frac{p}{d_1})\)\(\cdots\),直到 \(a,\frac{p}{d_1d_2\cdots d_k}\) 互质为止。

我们设一共进行了 \(k\) 次约化,\(D=d_1d_2\cdots d_k\)(此时 \(a\)\(\frac{p}{D}\) 互质),原式可变形为

\[\frac{a^k}{D}\cdot a^{x-k} \equiv \frac{b}{D} \pmod {\frac{p}{D}} \]

我们令 \(k=\frac{a^k}{D}, X=x-k, B=\frac{b}{D}, P=\frac{p}{D}\),即

\[ka^X\equiv B \pmod P \]

于是可以利用修改后的BSGS算法来求解。

注意求解之后得到的 \(X=x-k\),故 \(x=X+k\)

Trick

\(\displaystyle\frac{a^k}{D}=\frac{a}{d_1}\frac{a}{d_2}\cdots\frac{a}{d_k}\),于是可以在每一个循环内单独计算。

cout<<'\n' 似乎会比用 cout<<endl 快一些。

可以预先将 b%=p, a%=p,若取模过后 \(b=1\)\(p=1\),那么显然 \(x=0\) 为解。

我们记 \(\displaystyle D_k=\frac{a}{d1}\frac{a}{d2}\cdots \frac{a}{d_k}\),当 \(\displaystyle\frac{a^k}{D^k}=\frac{b}{D_k}\) 时,有

\[\frac{a^k}{D_k}\cdot a^{x-k}\equiv \frac{b}{D_k}\pmod {\frac{p}{D_k}} \]

\[a^{x-k}\equiv 1\pmod {\frac{p}{D_k}} \]

于是 \(x=k\) 为解。其中 \(k\) 是正在进行的第 \(k\) 次约化。

代码实现

#include <bits/stdc++.h>
using namespace std;

#define int long long
#define reg register
inline int qpow(int a, int n, int p) {
    a%=p; int res=1;
    while (n) {
        if (n&1) res=res*a%p;
        a=a*a%p; n>>=1;
    }
    return res;
}
inline int bsgs(int a, int b, int k, int p) {
    int t=(int)(sqrt(p))+1;
    unordered_map<int,int> h; h.clear();
    int powa=1;
    for (reg int j=0; j<t; ++j) {
        int val=powa*b%p;
        h[val]=j;
        powa=powa*a%p;
    }
    a=qpow(a,t,p);
    powa=1;
    for (reg int i=0; i<=t; ++i) {
        int val=k*powa%p;
        int j=h.find(val)==h.end()?-1:h[val];
        if (j>=0 && i*t-j>=0) return i*t-j;
        powa=powa*a%p;
    }
    return -1;
}
inline int exbsgs(int a, int b, int p) {
    a%=p, b%=p;
    if (b==1 || p==1) return 0;
    int A=a, NA=1, B=b, P=p, k=0, D=1;
    while (__gcd(a,P)>1) {
        int d=__gcd(a,P);
        if (B%d) return -1;
        k++; A/=d, B/=d, P/=d, NA=NA*(a/d)%P, D=D*d%P;
        if (NA==B) return k; // NA就是上文提到的(a^k)/(D^k)
    }
    int res=bsgs(a,B,NA,P);
    if (res==-1) return res;
    if ((qpow(a,res+k,p)-b)%p) return -1;
    return res+k;
}

signed main() {
    ios::sync_with_stdio(false);
    cin.tie(NULL); cout.tie(NULL);
    int a, b, p;
    while (cin>>a>>p>>b && a) {
        int res=exbsgs(a,b,p);
        if (res==-1) cout<<"No Solution\n";
        else cout<<res<<'\n';
    }
    return 0;
}

Record,\(2.46\rm{s}\),可以通过本题(包括Hack数据)。

后记

这道题算是比较毒瘤的,我是一共调了三天才过的(我太弱了)
还有 \(20\) 天就要中考了,而我还在这摸鱼(悲)
就谨此纪念一下我的初中OI生涯罢。

顺便在此祝福向宴酱中考顺利!文章来源地址https://www.toymoban.com/news/detail-470934.html

到了这里,关于(ex)BSGS/(扩展)大步小步算法 学习笔记的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 「学习笔记」BSGS

    点击查看目录 目录 「学习笔记」BSGS Baby-step Giant-step 问题 算法 例题 Discrete Logging 代码 P3306 [SDOI2013] 随机数生成器 思路 P2485 [SDOI2011]计算器 思路 Matrix 思路 代码 在 (O(sqrt{p})) 的时间内求解 [a^x equiv b pmod p] 其中 (aperp p) ,方程的解 (x) 满足 (0 le x p) 。 首先根据费马小

    2023年04月10日
    浏览(20)
  • 数论——欧几里得算法、裴蜀定理、扩展欧几里得算法 学习笔记

    最大公约数 最大公约数即为 Greatest Common Divisor,常缩写为 gcd。 一组整数的公约数,是指同时是这组数中每一个数的约数的数。 (pm 1) 是任意一组整数的公约数; 一组整数的最大公约数,是指所有公约数里面最大的一个。 特殊的,我们定义 (gcd(a, 0) = a) 。 最小公倍数 最

    2024年02月08日
    浏览(37)
  • 菜鸟教程《Python 3 教程》笔记 EX 01:命令行参数

    笔记带有个人侧重点,不追求面面俱到。 出处: 菜鸟教程 - Python3 命令行参数 Python 中可以所用 sys 的 sys.argv 来获取命令行参数: 注意: sys.argv[0] 为脚本名。 实例: test.py 文件: 运行结果: getopt 模块是专门处理命令行参数的模块,用于获取命令行选项和参数。该模块提供

    2024年02月10日
    浏览(33)
  • 「学习笔记」(扩展)中国剩余定理

    有物不知其数,三三数之剩二,五五数之剩三,七七数之剩二。问物几何? 该问题出自《孙子算经》,具体问题的解答口诀由明朝数学家程大位在《算法统宗》中给出: 三人同行七十希,五树梅花廿一支,七子团圆正半月,除百零五便得知。 (2 times 70 + 3 times 21 + 2 times

    2024年02月06日
    浏览(27)
  • MyBatisPlus学习笔记四-扩展功能

       先查询,再分组    

    2024年01月19日
    浏览(32)
  • 【005】ts学习笔记【函数扩展】

    参数类型 可选参数与默认值 接口定义函数 定义剩余参数 函数重载 定义 函数重载是指在 TypeScript 中定义多个具有相同名称但参数类型或参数数量不同的函数声明。 函数重载规则 1, 多个函数定义使用相同的函数名称 2, 函数参数的数量或类型必须有区别 3,如果参数类型不同

    2024年02月11日
    浏览(28)
  • 【Shell学习笔记】Bash的模式扩展

    Shell 接收到用户输入的命令以后,会根据空格将用户的输入,拆分成一个个词元(token)。然后,Shell 会扩展词元里面的特殊字符,扩展完成后才会调用相应的命令。 这种特殊字符的扩展,称为模式扩展(globbing)。其中有些用到通配符,又称为通配符扩展(wildcard expansion)

    2024年02月07日
    浏览(45)
  • 数论——中国剩余定理、扩展中国剩余定理 学习笔记

    中国剩余定理(Chinese Remainder Theorem,CRT) 求解如下形式的一元线性同余方程组(其中 (m) 两两互质): $left{begin{matrix}x equiv a_1 pmod {m_1} \\\\x equiv a_2 pmod {m_2} \\\\ dots \\\\x equiv a_k pmod {m_k}end{matrix}right.$ 计算所有模数的积 (M = prod m_i) ; 对于第 (i) 个方程: 计算: (M_i

    2024年02月08日
    浏览(34)
  • Kubernetes学习笔记-kubernetes应用扩展(1)-自定义API对象20230622

    1、CustomResourceDefinitions介绍 开发者只需要只需向kubernetes api服务器提交CRD对象,即可定义新的资源类型。成功提交CRD之后,就能通过API服务器提交JSON清单或者YAML清单的方式创建自定义资源,以及其他kubernetes资源实例 创建一个CRD对象 website-crd.yaml apiVersion:apiextensions.k8s.io/v1

    2024年02月10日
    浏览(34)
  • 嵌入式C语言自我修养《GNU C编译器扩展语法》学习笔记

    目录 一、C语言标准和编译器 二、指定初始化 三、宏构造“利器”:语句表达式 四、typeof与container_of宏 五、零长度数组 六、属性声明:section  七、属性声明:aligned  C语言标准的发展过程: ● KR C. ● ANSI C. ● C99. ● C11. 指定初始化结构体成员:         和数组类似,

    2024年02月08日
    浏览(42)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包