「算法笔记」BSGS 与 exBSGS
一、离散对数
给定 \(a,b,m\),存在一个 \(x\),使得
\(\displaystyle a^x\equiv b\pmod m\)
则称 \(x\) 为 \(b\) 在模 \(m\) 意义下以 \(a\) 为底的 离散对数。
二、BSGS
离散对数:求解关于 \(x\) 的方程 \(a^x\equiv b\pmod m\)。
基本思想:(假设 \(\gcd(a,m)=1\),那么 \(a\) 在模 \(m\) 意义下存在逆元)
考虑类似分块的一个想法。首先设定一个常量 \(t\)。
设 \(x=qt+r\)(\(0\leq r<t\)),预处理所有 \(a^{qt}\) 模 \(m\) 的值,存到 Hash 表 / map 中。询问时,枚举 \(r\),因为 \(a^{qt+r}\equiv b\pmod m\Leftrightarrow a^{qt}\equiv b\cdot a^{-r}\pmod m\),所以判断是否存在 \(a^{qt}\equiv b\cdot a^{-r}\pmod m\) 即可。
预处理的复杂度为 \(\mathcal{O}(\frac{m}{t})\),单次询问的复杂度为 \(\mathcal{O}(t)\)。取 \(t=\sqrt{m}\),则复杂度为 \(\mathcal{O}(\sqrt{m})\)。
用 map 会多一个 \(\log\)。
不同的写法:如果不想求 \(a^{-r}\),设 \(x=qt-r\)(\(0\leq q,r<t\)), \(a^{qt-r}\equiv b\pmod m\Leftrightarrow a^{qt}\equiv b\cdot a^r\pmod m\),预处理 \(b\cdot a^r\) 模 \(m\) 的值,枚举 \(q\),判断是否能找到对应的 \(r\)。
//Luogu P3846
#include<bits/stdc++.h>
#define int long long
using namespace std;
map<int,int>mp;
int a,b,p,ans;
int mul(int x,int n,int mod){
int ans=mod!=1;
for(x%=mod;n;n>>=1,x=x*x%mod)
if(n&1) ans=ans*x%mod;
return ans;
}
int BSGS(int a,int b,int p){ //设 x=qt-r,预处理 b*(a^r)%p,枚举 q,判断是否存在 a^{qt}%p=b*{a^r}%p
a%=p,b%=p,mp.clear();
if(!a) return !b?0:-1;
int t=ceil(sqrt(p)),x=b;
for(int i=0;i<=t;i++) mp[x]=i,x=x*a%p; //枚举 r,预处理 b*(a^r)%p 的值
a=mul(a,t,p),x=a;
for(int i=1;i<=t;i++){ //枚举 q
if(mp.count(x)) return i*t-mp[x]; //判断是否能找到对应的 r。若找到了,则 x=qt-r 为 x 的一个解
x=x*a%p;
}
return -1;
}
signed main(){
scanf("%lld%lld%lld",&p,&a,&b);
ans=BSGS(a,b,p);
if(~ans) printf("%lld\n",ans);
else puts("no solution");
return 0;
}
二、exBSGS
求解关于 \(x\) 的方程 \(a^x\equiv b\pmod m\)。\(m\) 可取任意数。
BSGS:\(\gcd(a,m)=1\),\(a\) 在模 \(m\) 意义下存在逆元,可令等式右侧出现 \(a\) 的幂次。
扩展 BSGS:考虑 \(\gcd(a,m)\neq 1\) 的情况。一个想法是,将它转化为 \(\gcd(a,m)=1\)。
设 \(d=\gcd(a,m)\)。\(a^x\equiv b\pmod m\Leftrightarrow a^x+km=b\),根据裴蜀定理,方程有解当且仅当 \(\gcd(a,m)\mid b\)。所以,若 \(d\nmid b\),则原方程无解。
否则,令方程两边同时除以 \(d\),得:
\(\displaystyle\frac{a}{d}\cdot a^{x-1}+k\cdot\frac{m}{d}=\frac{b}{d}\)
此时,若 \(\gcd(a,m)\neq 1\),则令 \(x'=x-1,m'=\frac{m}{d},b'=\frac{b}{d}\),重复上述步骤,于是可以一直做下去,直到 \(\gcd(a,m')=1\)。
不妨设重复了 \(g\) 次,每次求得的 \(d=\gcd(a,m')\) 分别为 \(d_1,d_2,\cdots,d_g\)。记 \(\prod_{i=1}^g d_i =D\),原式就是:
\(\displaystyle\frac{a^g}{D}\cdot a^{x-g}\equiv \frac{b}{D}\pmod {\frac{m}{D}}\)
那么就令 \(a'=a,b'=\frac{b}{D},m'=\frac{m}{D}\),跑一遍 BSGS 即可(在枚举 \(a^{qt}\) 判断时乘上 \(\frac{a^g}{D}\),对应代码中的 ad。最后答案加上 \(g\) 就行了)。
//Luogu P4195
#include<bits/stdc++.h>
#define int long long
using namespace std;
map<int,int>mp;
int a,b,p,ans;
int mul(int x,int n,int mod){
int ans=mod!=1;
for(x%=mod;n;n>>=1,x=x*x%mod)
if(n&1) ans=ans*x%mod;
return ans;
}
int BSGS(int a,int b,int p,int ad){
a%=p,b%=p,mp.clear();
if(!a) return !b?0:-1;
int t=ceil(sqrt(p)),x=b;
for(int i=0;i<=t;i++) mp[x]=i,x=x*a%p;
a=mul(a,t,p),x=a*ad%p; //upd: 原来的 x=a 改为了 x=a*ad%p
for(int i=1;i<=t;i++){
if(mp.count(x)) return i*t-mp[x];
x=x*a%p;
}
return -1;
}
int exBSGS(int a,int b,int p){
a%=p,b%=p;
int g=0,ad=1,d,ans;
while((d=__gcd(a,p))!=1){
if(b%d) return -1;
g++,b/=d,p/=d,ad=(ad*a/d)%p;
if(ad==b) return g;
}
if(~(ans=BSGS(a,b,p,ad))) return ans+g;
return -1;
}
signed main(){
while(~scanf("%lld%lld%lld",&a,&p,&b)){
if(!a&&!b&&!p) break;
ans=exBSGS(a,b,p);
if(~ans) printf("%lld\n",ans);
else puts("No Solution");
}
return 0;
}
建议把 map 改成 Hash 表 QAQ。
「算法笔记」BSGS 与 exBSGS的更多相关文章
- 「算法笔记」快速数论变换(NTT)
一.简介 前置知识:多项式乘法与 FFT. FFT 涉及大量 double 类型数据操作和 \(\sin,\cos\) 运算,会产生误差.快速数论变换(Number Theoretic Transfo ...
- 「算法笔记」树形 DP
一.树形 DP 基础 又是一篇鸽了好久的文章--以下面这道题为例,介绍一下树形 DP 的一般过程. POJ 2342 Anniversary party 题目大意:有一家公司要举行一个聚会,一共有 \ ...
- 「算法笔记」2-SAT 问题
一.定义 k-SAT(Satisfiability)问题的形式如下: 有 \(n\) 个 01 变量 \(x_1,x_2,\cdots,x_n\),另有 \(m\) 个变量取值需要满足的限制. 每个限 ...
- 「算法笔记」Polya 定理
一.前置概念 接下来的这些定义摘自 置换群 - OI Wiki. 1. 群 若集合 \(s\neq \varnothing\) 和 \(S\) 上的运算 \(\cdot\) 构成的代数结构 \((S, ...
- 「算法笔记」状压 DP
一.关于状压 dp 为了规避不确定性,我们将需要枚举的东西放入状态.当不确定性太多的时候,我们就需要将它们压进较少的维数内. 常见的状态: 天生二进制(开关.选与不选.是否出现--) 爆搜出状态,给它 ...
- 「算法笔记」旋转 Treap
一.引入 随机数据中,BST 一次操作的期望复杂度为 \(\mathcal{O}(\log n)\). 然而,BST 很容易退化,例如在 BST 中一次插入一个有序序列,将会得到一条链,平均每次操作的 ...
- 「算法笔记」FHQ-Treap
右转→https://www.cnblogs.com/mytqwqq/p/15057231.html 下面放个板子 (禁止莱莱白嫖板子) P3369 [模板]普通平衡树 #include<bit ...
- 「算法笔记」Min_25 筛
戳 这里(加了密码).虽然写的可能还算清楚,但还是不公开了吧 QwQ. 真的想看的 私信可能会考虑给密码 qwq.就放个板子: //LOJ 6053 简单的函数 f(p^c)=p xor c #inc ...
- 「算法笔记」快速傅里叶变换(FFT)
一.引入 首先,定义多项式的形式为 \(f(x)=\sum_{i=0}^n a_ix^i\),其中 \(a_i\) 为系数,\(n\) 为次数,这种表示方法称为"系数表示法",一个 ...
随机推荐
- nodejs-CommonJS规范
JavaScript 标准参考教程(alpha) 草稿二:Node.js CommonJS规范 GitHub TOP CommonJS规范 来自<JavaScript 标准参考教程(alpha) ...
- 『学了就忘』Linux启动引导与修复 — 70、grub启动引导程序的配置文件说明
目录 1.grub中分区的表示方法 2.grub的配置文件 3.grub的配置文件内容说明 (1)grub的整体设置 (2)CentOS系统的启动设置 1.grub中分区的表示方法 在说grub启动引 ...
- Linux基础命令---host域名查询工具
host host是一个常用的DNS查询工具,经常用来查询域名.检查域名解析是否正确. 此命令的适用范围:RedHat.RHEL.Ubuntu.CentOS.Fedora. 1.语法 ...
- vue实现input输入框的模糊查询
最近在用uni-app做一个项目,使用的框架还是vue,想了好久才做出来 . HTML代码部分 <input type="text" focus class="s ...
- Docker(4)-docker常用命令
帮助命令 docker version # 查看docker的版本信息 docker info # 查看docker的系统信息,包含镜像和容器的数量 docker --help # 帮助命令 dock ...
- 从 CPython 源码角度看 Python 垃圾回收机制
环状双向链表 refchain 在 Python 程序中创建的任何对象都会被放到 refchain 链表中,当创建一个 Python 对象时,内部实际上创建了一些基本的数据: 上一个对象 下一个对象 ...
- ciscn_2019_s_4***(栈迁移)
这是十分经典的栈迁移题目 拿到题目例行检查 32位程序开启了nx保护 进入ida,发现了很明显的system 我们进入main函数查看vul 可以看到溢出的部分不够我们rop所以这道题通过栈迁移去做 ...
- UNCTF2020 pwn题目
YLBNB 用pwntools直接连接,然后接受就行. 1 from pwn import * 2 3 p = remote('45.158.33.12',8000) 4 context.log_le ...
- 联盛德 HLK-W806 (十): 在 CDK IDE开发环境中使用WM-SDK-W806
目录 联盛德 HLK-W806 (一): Ubuntu20.04下的开发环境配置, 编译和烧录说明 联盛德 HLK-W806 (二): Win10下的开发环境配置, 编译和烧录说明 联盛德 HLK-W ...
- Java中List排序的3种方法
在某些特殊的场景下,我们需要在 Java 程序中对 List 集合进行排序操作.比如从第三方接口中获取所有用户的列表,但列表默认是以用户编号从小到大进行排序的,而我们的系统需要按照用户的年龄从大到小进 ...