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,随便水过后我决定加一道升级版,说是升级版,其实也没什么变化,只 ...
随机推荐
- poj3061(尺取法)
---恢复内容开始--- 题目意思:给你一段非负序列,再给你一个值k,找出这段序列中最少的连续子序列使得和为k: 解题思路:因为都是正数,我们只需要找到一段区间不大于k,就停止,然后左边趋近看是否能得 ...
- day12 max min zip 用法
max min ,查看最大值,最小值 基础玩法 l = [1,2,3,4,5] print(max(l)) print(min(l)) 高端玩法 默认字典的取值是key的比较 age_dic={'al ...
- CodeForces 1110F Nearest Leaf | 线段树/换根
我--又诈尸了-- 代码几乎都不会写了,打场CF居然上分啦,开心!(虽然还是比不过列表里的各路神仙) 题目链接 题目描述 一棵\(n\)个点的有根树,规定一种dfs序(规则:编号小的点优先dfs),\ ...
- luogu P2181 对角线
题目大意: 给一个n边形,求出在所有任意三条对角线都不相交于同一个点的情况下,交点个数是多少.(即交点个数最多是多少) 分析: 题目很水,但是公式不好想. 由于任意三条对角线不会交于一点,所以所有的交 ...
- MFC中运行出现问题“不支持尝试执行的操作”
http://blog.csdn.net/maturn/article/details/8051987 问题描述: 基于CDialogEx的对话框工程.VS2010开发环境. 调试运行到OnInitD ...
- 转:@ControllerAdvice + @ExceptionHandler 全局处理 Controller 层异常
继承 ResponseEntityExceptionHandler 类来实现针对 Rest 接口 的全局异常捕获,并且可以返回自定义格式: 复制代码 1 @Slf4j 2 @ControllerAdv ...
- Hadoop生态圈-使用Ganglia监控flume中间件
Hadoop生态圈-使用Ganglia监控flume中间件 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.Ganglia监控简介 加州伯克利大学千禧计划的其中一个开源项目.是一 ...
- OSPF基础介绍
OSPF基础介绍 一.RIP的缺陷 1.以跳数评估的路由并非最优路径 2.最大跳数16导致网络尺度小 3.收敛速度慢 4.更新发送全部路由表浪费网络资源 二.OSPF基本原理 1.什么是OSPF a& ...
- CentOS下的yum upgrade和yum update区别
说明:生产环境对软件版本和内核版本要求非常精确,别没事有事随便的进行yum update操作!! ! yum update:升级所有包同时也升级软件和系统内核 yum upgrade:只升级所有包,不 ...
- UDP网路会议室的代码
UDP网络会议室视频已经录制好,这里贴上代码仅供参考 MainWindow代码: using System; using System.Collections.Generic; using Syste ...