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;//这句话其实可以省。
}

例题:

洛谷 P4884 多少个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算法及其扩展的更多相关文章

  1. BSGS算法及扩展

    BSGS算法 \(Baby Step Giant Step\)算法,即大步小步算法,缩写为\(BSGS\) 拔山盖世算法 它是用来解决这样一类问题 \(y^x = z (mod\ p)\),给定\(y ...

  2. BSGS算法学习笔记

    从这里开始 离散对数和BSGS算法 扩展BSGS算法 离散对数和BSGS算法 设$x$是最小的非负整数使得$a^{x}\equiv b\ \ \ \pmod{m}$,则$x$是$b$以$a$为底的离散 ...

  3. BSGS算法总结

    BSGS算法总结 \(BSGS\)算法(Baby Step Giant Step),即大步小步算法,用于解决这样一个问题: 求\(y^x\equiv z\ (mod\ p)\)的最小正整数解. 前提条 ...

  4. BSGS算法+逆元 POJ 2417 Discrete Logging

    POJ 2417 Discrete Logging Time Limit: 5000MS   Memory Limit: 65536K Total Submissions: 4860   Accept ...

  5. BSGS算法解析

    前置芝士: 1.快速幂(用于求一个数的幂次方) 2.STL里的map(快速查找) 详解 BSGS 算法适用于解决高次同余方程 \(a^x\equiv b (mod p)\) 由费马小定理可得 x &l ...

  6. 【codevs 1565】【SDOI 2011】计算器 快速幂+拓展欧几里得+BSGS算法

    BSGS算法是meet in the middle思想的一种应用,参考Yveh的博客我学会了BSGS的模版和hash表模板,,, 现在才会hash是不是太弱了,,, #include<cmath ...

  7. bzoj2242: [SDOI2011]计算器 && BSGS 算法

    BSGS算法 给定y.z.p,计算满足yx mod p=z的最小非负整数x.p为质数(没法写数学公式,以下内容用心去感受吧) 设 x = i*m + j. 则 y^(j)≡z∗y^(-i*m)) (m ...

  8. 浅谈Manacher算法与扩展KMP之间的联系

    首先,在谈到Manacher算法之前,我们先来看一个小问题:给定一个字符串S,求该字符串的最长回文子串的长度.对于该问题的求解.网上解法颇多.时间复杂度也不尽同样,这里列述几种常见的解法. 解法一   ...

  9. [BSGS算法]纯水斐波那契数列

    学弟在OJ上加了道"非水斐波那契数列",求斐波那契第n项对1,000,000,007取模的值,n<=10^15,随便水过后我决定加一道升级版,说是升级版,其实也没什么变化,只 ...

随机推荐

  1. codeforces 873C - Strange Game On Matrix

    题目大意:给你一个n*m的只有0和1的矩阵,找到每列第一个1的位置a[i][j],a[i][j]及其a[min(k,n-i+1][j]中1的数量,每列位置值是1的可以变为0: 解题思路:因为数据较小, ...

  2. ajax 提交数组 泛型集合(二)

    最近在项目中,使用 mvc架构,model层使用code first 碰见一个问题,前台json传递数据给后台action的复杂对象,发现复杂对象中的list范型集合并没有获取到数据. 研究半天,终于 ...

  3. Hibernate基本应用01

    一. Hibernate简介 1.1 Hibernate介绍 Hibernate是一个开放源代码的对象关系映射框架,它对JDBC进行了非常轻量级的对象封装,它将POJO与数据库表建立映射关系,是一个全 ...

  4. Java中的split函数拆分后变成null的问题

    对于一些特殊的情况,split拆分后并没有保留全的元素 如下例子: String x ="a,,,,,,"; String []y = x.split(",") ...

  5. [luogu3919]可持久化数组【主席树】

    链接:https://www.luogu.org/problemnew/show/P3919 分析 很明显我们可以用主席树来维护,所谓主席树就是可持久化线段树,能够查询历史版本而且可以实现修改操作,反 ...

  6. 【转】C语言字符串与数字相互转换

    在C/C++语言中没有专门的字符串变量,通常用字符数组来存放字符串.字符串是以“\0”作为结束符.C/C++提供了丰富的字符串处理函数,下面列出了几个最常用的函数. ● 字符串输出函数puts. ● ...

  7. JDK源码分析(4)HashSet

    JDK版本 HashSet简介 HashSet特点 非线程安全 允许null值 添加值得时候会先获取对象的hashCode方法,如果hashCode 方法返回的值一致,则再调用equals方法判断是否 ...

  8. 「loj3058」「hnoi2019」白兔之舞

    题意 有一个\((L+1)*n\) 的网格图,初始时白兔在\((0,X)\) , 每次可以向横坐标递增,纵坐标随意的位置移动,两个位置之间的路径条数只取决于纵坐标,用\(w(i,j)\) 表示,如果要 ...

  9. 几个简单常用的Sql语句

    '; --查Cids为2的Gnumber列的和,列名为Ids select Cids,Plevel from People; select * from Salary; select * from S ...

  10. C# 数组&集合&泛型集合

    一.数组 必须规定类型,必须规定长度:  1.定义 int[ ] i = new int[5]; int[] j = new int[]{1,2,3,4,5}; 2.数组的遍历: Console.Wr ...