「算法笔记」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\) 为次数,这种表示方法称为"系数表示法",一个 ...
随机推荐
- A Child's History of England.33
To strengthen his power, the King with great ceremony betrothed his eldest daughter Matilda, then a ...
- celery开启worker报错django.core.exceptions.ImproperlyConfigured: Requested setting INSTALLED_APPS, but settings are not configured. You must either define the environment variable DJANGO_SETTINGS_MODULE o
其实挺简单的问题,但花了自己一个下午来解决,先是浏览各种博客,无果:没办法,然后去看celery官方文档,无果,近乎绝望,最后仔细看代码,找到问题所在(如下),自学狗这效率...... 下面是自己ta ...
- const与指针的三种形式
使用指针时涉及到两个对象:该指针本身和被它所指的对象. 将一个指针的声明用const"预先固定"将使那个对象而不是使这个指针成为常量.要将指针本身而不是被指对象声明为常量,必须使用 ...
- 【STM32】使用SDIO进行SD卡读写,包含文件管理FatFs(终)-配合内存管理来遍历SD卡
[STM32]使用SDIO进行SD卡读写,包含文件管理FatFs(一)-初步认识SD卡 [STM32]使用SDIO进行SD卡读写,包含文件管理FatFs(二)-了解SD总线,命令的相关介绍 [STM3 ...
- mysql explain using index condition
Using where:表示优化器需要通过索引回表查询数据:Using index:表示直接访问索引就足够获取到所需要的数据,不需要通过索引回表:Using index condition:在5.6版 ...
- OpenStack之之一: 快速添加计算节点
根据需求创建脚本,可以快速添加节点#:初始化node节点 [root@node2 ~]# systemctl disable NetworkManager [root@node2 ~]# vim /e ...
- ubantu安装maven
下载地址 http://maven.apache.org/download.cgi 或直接命令行下载 wget https://downloads.apache.org/maven/maven-3/3 ...
- Does compiler create default constructor when we write our own?
In C++, compiler by default creates default constructor for every class. But, if we define our own c ...
- 【Github】如何下载csv文件/win10如何修改txt文件为csv文件
csv文件:逗号分隔值(Comma-Separated Values,CSV,有时也称为字符分隔值,因为分隔字符也可以不是逗号) 右键点击raw按钮,选择目标另存为,下载的是txt文件 win10如何 ...
- 【笔记】草履虫也能看懂的ELK搭建流程
环境需要 Elasticsearch需要JAVA环境,至少是JDK1.8 elasticsearch 不允许root用户使用,需要新增个elk用户 如果觉得官网下载太慢,可以使用这个 https:// ...