BSGS算法及其扩展
bsgs算法:
我们在逆元里曾经讲到过如何用殴几里得求一个同余方程的整数解。而 $ bsgs $ 就是用来求一个指数同余方程的最小整数解的:也就是对于 $ a^x\equiv b \mod p $ 我们可以用 $ bsgs $ 在 $ O(\sqrt n) $ 的复杂度内求出关于 $ x $ 的最小正整数解。(前提是 $ p $ 为质数)
$ a^x\equiv b \mod p $
我们可以知道如果我们的模数p是一个质数,我们将同余式的右边以逆元的形式乘到左边来,根据殴拉定理(因为p是质数,所以a,p互质)则我们的x一定有一个小于p的正整数解!(当然,我们还要特判一下a是p倍数这一情况)。如果我们用穷举法来求这一个x最小是多少,复杂度显然不能接受,而 $ bsgs $ 就是做到了用空间来换取时间,对这个穷举进行一个改进。
其实这里 $ bsgs $ 的用空间换时间,和我们搜索用的双向搜是一个原理的。它充分利用了同余的性质,我们设m为 $ \sqrt a $ 向上取整,然后原式左边就变成了 $ ax=(am)^i\times a^j _{x=i+j} $ 这样我们第一步从小到大枚举i是多少,然后算出 $ (am)i $ ,然后根据同余方程我们就可以知道我们还需要的 $ a^j $ 是多少。于是第二步我们就可以先预处理出所有 $ a^j $ 然后存到hash表里,然后每算一个 $ (am)i $ 我们就在hash表中查看有没有对应的 $ a^j $ 存在(存hash的同时要保留 $ j $ 的值).这样我们整个算法的复杂度就是 $ O(\sqrt n) $ 了!
$ code: $
inline ll bsgs(int x,ll y,ll p){
int z=pow(p,0.5)+1;
ll sx=1,sy=1;
for(rg i=0;i<z;++i,sx=ksc(sx,x,p))
ha1(i,ksc(sx,y,p));//先把所有的a^j存入hash表
sy=1;
for(rg i=0;i<=z;++i,sy=ksc(sy,sx,p)){//可能用到快速乘
for(rg j=tou[sy%376637];j;j=b[j].p)//查看hash表中是否有相应的a^j
if(sy==b[j].y)return (ll)i*z-b[j].x;
}return p-1;//这句话其实可以省。
}
例题:

$ solution $ :
这道题还是比较裸的,我们可以讲题目中的同余方程左右同乘9再加1,得到: $ 10^x\equiv k\times 9+1\mod m $ 。这样就直接变成了一道板子题了(不过bsgs的题目向来是要用到快速乘的):
#include<iostream>
#include<cstdio>
#include<iomanip>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<ctime>
#include<cmath>
#include<vector>
#include<queue>
#include<map>
#include<set>
#define lb long double
#define ll long long
#define db double
#define rg register int
using namespace std;
struct su{
int p,x;
ll y;
}b[376637];
int top;
ll k,m;
int tou[376637];
inline ll qr(){//快读
char ch;
while((ch=getchar())<'0'||ch>'9');
ll res=ch^48;
while((ch=getchar())>='0'&&ch<='9')
res=res*10+(ch^48);
return res;
}
inline ll ksc(ll x,ll y,ll p){//快速乘
return (x*y-(ll)((lb)x/p*y)*p+p)%p;
}
inline void ha1(int x,ll y){//y用链式前向星来hash
b[++top].x=x; b[top].y=y; y%=376637;
b[top].p=tou[y]; tou[y]=top;
}
inline ll bsgs(int x,ll y,ll p){//快速乘
int z=pow(p,0.5)+1;
ll sx=1,sy=1;
for(rg i=0;i<z;++i,sx=ksc(sx,x,p))
ha1(i,ksc(sx,y,p));
sy=sx;
for(rg i=1;i<=z;++i,sy=ksc(sy,sx,p)){
for(rg j=tou[sy%376637];j;j=b[j].p)
if(sy==b[j].y)return (ll)i*z-b[j].x;
}return p-1;
}
int main(){
//freopen(".in","r",stdin);
//freopen(".out","w",stdout);
k=qr(); m=qr(); k=(k*9+1)%m;//稍稍处理一下
printf("%lld\n",bsgs(10,k,m));
return 0;
}
$ BSGS $ 算法的扩展:
上面我们有讲过一般的 $ BSGS $ 算法是有前提条件的(我们的p必须是素数)。但是当我们的p不是素数的时候我们也是可以用 $ BSGS $ 求出来的,只是要先做处理。因为同余具有这样一条神奇的性质(同除性): $ a\equiv b \mod m \quad ->\quad d=gcd(a,b,p) \quad-> \quad \frac{a}{d} \equiv \frac{b}{d}\mod \frac{m}{d} $
根据这条性质,我们可以将a,p 取gcd 。而在扩展欧几里得解一次同余方程中我们说过,如果要有一组整数解,冲要条件就是我们的gcd(a,p)可以整除b!所以如果我们在BSGS中给三者用同除时如果b不能为整数了则无解!对这个进行特判后我们就可以一直同除(注意我们的同余式左边可以是多个a的乘积1)直到 $ a^x $ 与 $ p $ 互质。这时我们就可以再次 $ BSGS $ 来求解了(注意我们此时只保证 $ a^x $ 与 $ p $ 互质,而不是p为素数,所以可能无解!)
$ code $ :
inline int bsgs(int x,int y,int p,int ex){
int z=sqrt(p)+1; ll sx=1,sy;
for(rg i=0;i<z;++i)
ha(i,sx*y%p),sx=sx*x%p;
sy=sx;
for(rg i=1;i<=z;++i,sy=sy*sx%p)
for(rg j=tou[sy%72727];j;j=b[j].p)
if(b[j].y==sy)return i*z-b[j].x;
return -1;//可能无解
}
例题:
洛谷 P4195 【模板】exBSGS/Spoj3105 Mod

这确确实实就是道模板,不过是多组数据,所以我们用手写hash防被卡:
#include<iostream>
#include<cstdio>
#include<iomanip>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<ctime>
#include<cmath>
#include<vector>
#include<queue>
#include<map>
#include<set>
#define lb long double
#define ll long long
#define db double
#define inf 0x7fffffff
#define rg register int
using namespace std;
struct su{
int x,y,p;
}b[72727];
int n,m,mod,ans;
int top,tou[72727];
inline int qr(){
char ch;
while((ch=getchar())<'0'||ch>'9');
int res=ch^48;
while((ch=getchar())>='0'&&ch<='9')
res=res*10+(ch^48);
return res;
}
inline int gcd(int x,int y){
int z;
while(y){z=x,x=y,y=z%y;}
return x;
}
inline int ksm(ll x,int y,int p){
int res=1;
while(y){
if(y&1)res=res*x%p;
x=x*x%p; y>>=1;
}return res;
}
inline void ha(int x,int y){
b[++top].x=x; b[top].y=y; y%=72727;
b[top].p=tou[y]; tou[y]=top;
}
inline int bsgs(int x,int y,int p,int ex){
int z=sqrt(p)+1; ll sx=1,sy;
for(rg i=0;i<z;++i)
ha(i,sx*y%p),sx=sx*x%p;
sy=sx*ex%p;
for(rg i=1;i<=z;++i,sy=sy*sx%p)
for(rg j=tou[sy%72727];j;j=b[j].p)
if(b[j].y==sy)return i*z-b[j].x;
return -1;
}
inline int ex_bsgs(int x,int y,int p){
if(y==1)return 0;
ll c=1,z=x,d; int k=0;
while(1){
d=gcd(x,p);
if(d==1)break;
if(y%d)return -1;
++k; c=c*(x/d)%p;
y/=d; p/=d;
if(c==y)return k;
}z=bsgs(x,y,p,c);
return z==-1?z:z+k;
}
signed main(){
//freopen(".in","r",stdin);
//freopen(".out","w",stdout);
while(n=qr(),mod=qr(),m=qr(),n){top=0;
for(rg i=0;i<72727;++i)tou[i]=0;
ans=ex_bsgs(n%mod,m%mod,mod);
if(ans!=-1)printf("%d\n",ans);
else puts("No Solution");
}
return 0;
}
BSGS算法及其扩展的更多相关文章
- BSGS算法及扩展
BSGS算法 \(Baby Step Giant Step\)算法,即大步小步算法,缩写为\(BSGS\) 拔山盖世算法 它是用来解决这样一类问题 \(y^x = z (mod\ p)\),给定\(y ...
- BSGS算法学习笔记
从这里开始 离散对数和BSGS算法 扩展BSGS算法 离散对数和BSGS算法 设$x$是最小的非负整数使得$a^{x}\equiv b\ \ \ \pmod{m}$,则$x$是$b$以$a$为底的离散 ...
- BSGS算法总结
BSGS算法总结 \(BSGS\)算法(Baby Step Giant Step),即大步小步算法,用于解决这样一个问题: 求\(y^x\equiv z\ (mod\ p)\)的最小正整数解. 前提条 ...
- BSGS算法+逆元 POJ 2417 Discrete Logging
POJ 2417 Discrete Logging Time Limit: 5000MS Memory Limit: 65536K Total Submissions: 4860 Accept ...
- BSGS算法解析
前置芝士: 1.快速幂(用于求一个数的幂次方) 2.STL里的map(快速查找) 详解 BSGS 算法适用于解决高次同余方程 \(a^x\equiv b (mod p)\) 由费马小定理可得 x &l ...
- 【codevs 1565】【SDOI 2011】计算器 快速幂+拓展欧几里得+BSGS算法
BSGS算法是meet in the middle思想的一种应用,参考Yveh的博客我学会了BSGS的模版和hash表模板,,, 现在才会hash是不是太弱了,,, #include<cmath ...
- bzoj2242: [SDOI2011]计算器 && BSGS 算法
BSGS算法 给定y.z.p,计算满足yx mod p=z的最小非负整数x.p为质数(没法写数学公式,以下内容用心去感受吧) 设 x = i*m + j. 则 y^(j)≡z∗y^(-i*m)) (m ...
- 浅谈Manacher算法与扩展KMP之间的联系
首先,在谈到Manacher算法之前,我们先来看一个小问题:给定一个字符串S,求该字符串的最长回文子串的长度.对于该问题的求解.网上解法颇多.时间复杂度也不尽同样,这里列述几种常见的解法. 解法一 ...
- [BSGS算法]纯水斐波那契数列
学弟在OJ上加了道"非水斐波那契数列",求斐波那契第n项对1,000,000,007取模的值,n<=10^15,随便水过后我决定加一道升级版,说是升级版,其实也没什么变化,只 ...
随机推荐
- CentOS常用包安装
安装rz和sz命令: yum install lrzsz 安装unzip:yum install unzip
- Bootstrap洼地
前面的话 这是一个轻量.灵活的组件,它能延伸至整个浏览器视口来展示网站上的关键内容.本文将详细介绍Bootstrap洼地 概述 洼地(Well)样式的效果和巨幕jumbotron样式类似,不同点是we ...
- solr string类型表示不支持分词
solr string类型表示不支持分词
- 学习记录特别篇之sql,类的继承
思路: 应用场景: 1.将父类当做一个基础类 大家都去继承该方法,以便少些代码 2.继承父类的方法 同时可以重写该方法时候调用父类原先的方法 实现一石二鸟的效果 即 既增加原先的功能 又新增新的功能 ...
- python----面对对象三大特征2
多态 什么是多态?多态指的是一类事物有多种形态,例如一个动物类,狗和猫都通过继承动物类来得来,这样我们可以将狗.猫称作动物类的另一种形态. 在java中也存在多态,java中的多态是用来解决在一个函数 ...
- POJ1019-Number Sequence-数数。。
1 12 123 1234 把数按照这样的形式拍成一排,给一个序号求出那个序号对应的数. 当出现两位数.三位数时,要麻烦的处理一下. #include <cstdio> #include ...
- day 变量的赋值原理 变量的命名规则
print(3*4*5*6/2) #简单的计算输出 x=3 y=4 print("x乘以y=",x*y) #运行5分钟 = 12 print(x*y) #在运行5分钟 = 12 ' ...
- FFT/NTT总结+洛谷P3803 【模板】多项式乘法(FFT)(FFT/NTT)
前言 众所周知,这两个东西都是用来算多项式乘法的. 对于这种常人思维难以理解的东西,就少些理解,多背板子吧! 因此只总结一下思路和代码,什么概念和推式子就靠巨佬们吧 推荐自为风月马前卒巨佬的概念和定理 ...
- Bash: about .bashrc, .bash_profile, .profile, /etc/profile, etc/bash.bashrc and others
Some interesting excerpts from the bash manpage:When bash is invoked as an interactive login shell, ...
- Linux中无权限使用sudo
提示:$USER is not in the sudoers file. This incident will be reported. 解决方法: 切换到root用户,su 编辑sudo配置文件,v ...