Processing math: 5%

浅谈BSGS(大步小步)及其扩展

用途:

一般用来求a^x\equiv b\,\,(mod\,p)的最小正整数解,其中gcd(a,p)=1

u=\lceil sqrt(p)\rceil,则式子可以转化为a^{iu-j}\equiv b\,\,(mod\,p),其中i\in[1,u],j\in[0,u)

于是a^{iu}\equiv a^jb\,\,(mod\,p),我们就可以枚举j,存到map中,再枚举i判重就行了

不过当存在不同的j使a^jb\,mod\,p相同时,我们记录较大的(因为j越大答案越小嘛)

简易原理:

费马小定理:若gcd(x,p)=1,则有\\x^{p-1}\equiv1\,\,(mod\,p),得到x^p\equiv x\,\,(mod\,p)

所以当指数不小于p时,mod p的值会形成循环

Code:

板子题:luogu P4028 New Product

#include<bits/stdc++.h>
#define ll long long
using namespace std;
map<int,int> mp;
int read(){
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')f=-f;ch=getchar();}
    while(isdigit(ch)){x=x*10+ch-48;ch=getchar();}
    return x*f;
}
int quickpow(int a,int b,int p){
	int re=1;
	while(b){if(b&1) re=1ll*re*a%p;a=1ll*a*a%p;b>>=1;}
	return re;
}
void solve(){
	int p=read(),a=read(),b=read();
	if(a%p==0){puts("Couldn't Produce!");return ;}
	if(b==1){puts("0");return ;}
	int u=sqrt(p)+1,v=b;mp.clear();
	for(int i=0;i<u;i++)
        mp[v]=i,v=1ll*v*a%p;
    int w=quickpow(a,u,p);v=1;
    for(int i=1;i<=u;i++){v=1ll*v*w%p;
        if(mp.count(v)){
			printf("%d\n",i*u-mp[v]);
			return ;
		}
    }puts("Couldn't Produce!");
}
int main(){
	int T=read();
	while(T--) solve();
	return 0;
}

扩展:

可以看到BSGS是有着局限性的,即必须满足gcd(a,p)=1

那么当gcd(a,p)!=1时呢?我们设d=gcd(a,p)

Step1:

​ 我们首先判断b是否满足d|b,若不满足,由裴蜀定理可知无解

Step2:

​ 式子转化为:

a^{x-k}\frac{a^k}{\Pi_{i=1}^kd_i}\equiv \frac{b}{\Pi_{i=1}^kd_i}\,\,(mod\,\,\frac{p}{\Pi_{i=1}^kd_i})

​ 令c=\frac{a^k}{\Pi_{i=1}^kd_i},b'=\frac{b}{\Pi_{i=1}^kd_i},p‘=\frac{p}{\Pi_{i=1}^kd_i},若c=b',则直接输出k

Step3:

​ 令d=gcd(a,p'),若d\ne 1,则返回step1,不过b变成了b'

全部完成后,我们得到式子:a^{x-k}c\equiv b'\,\,(mod\,\,p'),此时满足gcd(a,p')=1

那么我们便可以直接BSGS了

Code:

板子题:luogu P4195 exBSGS

#include<bits/stdc++.h>
#include<unordered_map>
#define ll long long
using namespace std;
unordered_map<int,int> mp;
inline int read(){
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')f=-f;ch=getchar();}
    while(isdigit(ch)){x=x*10+ch-48;ch=getchar();}
    return x*f;
}
inline int gcd(int x,int y){
    if(y>x) swap(x,y);
    while(y){swap(x,y);y=y%x;}
    return x;
}
inline int quickpow(int a,int b,int p){
    int re=1;
    while(b){if(b&1) re=1ll*re*a%p;a=1ll*a*a%p;b>>=1;}
    return re;
}
inline void solve(int a,int p,int b){
    if(p==1){puts("0");return ;}
    if(b==1){puts("0");return ;}
    int d=gcd(a,p),flag=0,k=0,c=1;
    while(d^1){
        if(b%d){
            puts("No Solution");
            flag=1;break;
        }p/=d,b/=d,++k;
        c=1ll*c*(a/d)%p;
        if(b==c){
            printf("%d\n",k);
            flag=1;break;
        }d=gcd(a,p);
    }if(flag) return ;
    mp.clear();
    int u=sqrt(p)+1,v=b;
    for(int i=0;i<u;i++)
        mp[v]=i,v=1ll*v*a%p;
    v=c,c=quickpow(a,u,p);
    for(int i=1;i<=u;i++){
        v=1ll*v*c%p;
        if(mp.count(v)){
            printf("%d\n",i*u-mp[v]+k);
            return ;
        }
    }puts("No Solution");
}
int main(){
    while(1){
        int a=read(),p=read(),b=read();
        if(a==0&&b==0&&p==0) return 0;
        a%=p;b%=p;solve(a,p,b);
    }
}

话说为什么unordered_map比map快这么多啊,不过有时候编译会出锅...

posted @   DQY_dqy  阅读(228)  评论(0编辑  收藏  举报
编辑推荐:
· Linux系列:如何调试 malloc 的底层源码
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
阅读排行:
· Apifox不支持离线,Apipost可以!
· 零经验选手,Compose 一天开发一款小游戏!
· 历时 8 年,我冲上开源榜前 8 了!
· Trae 开发工具与使用技巧
· 通过 API 将Deepseek响应流式内容输出到前端
点击右上角即可分享
微信分享提示