扩展BSGS 学习笔记

首先你在学扩展BSGS前需要先了解BSGS。如果你还不了解BSGS或者对BSGS有什么疑问,可以看看我的BSGS讲解,我觉得基本是全网最详细的了。
我们知道,BSGS可以解决求 a x = b ( m o d   p ) 的最小非负整数 x ,它的应用条件是要求底数 a 与模数 p 互质的,那么如果不互质应该怎么办呢?这时候就要用到扩展BSGS了。
扩展BSGS是在BSGS的基础上进行一些改进。既然互质的我们会做了,我们就考虑把不互质的转化为互质的。
我们设 d = g c d ( a , p ) ,如果 d 不能整除 b ,那么只有 b = 1 时有解,即 x = 0 ,否则就无解。
首先先说明一个定理:

a = b ( m o d   p )
那么
a c = b c ( m o d   p c )

好,接下来开始我们的推导。
如果 d | b d 1 ,那么
a x = b ( m o d   p )
a x 1 a = b ( m o d   p )
a x 1 a d = b d ( m o d   p d )
如果此时的 a p d 仍然不互质,那么我们就继续分解,直到 a p i = 1 k d i 互质。
此时
a x k a k i = 1 k d i = b i = 1 k d i ( m o d   p i = 1 k d i )

对于已经求导的任何一个 k ,如果 i k d i 不能整除 b ,那么唯一可能的解就是 x = 0 ,所以如果我们在开头先特判了 x = 0 的情况,在这里只要遇到不能整除的情况就是无解。
对于 ( i = 1 k d i ) | b ,我们首先枚举 x [ 1 , k ] 是否是解( x = 0 已经提前特判了),如果是解就输出先在枚举到的 x ,这个 x l o g 2 b 级别的。
否则的话,一种做法是本来BSGS的 a n s 一开始是设为 1 的,我们这里把一开始的 a n s 的初值设为 a k i = 1 k d i ,然后BSGS。另一种做法是我自己yy的,我们对于
a x k a k i = 1 k d i = b i = 1 k d i ( m o d   p i = 1 k d i )
可以求出 a k i = 1 k d i 的逆元乘到右边去,然后BSGS。
经洛谷数据测试两种写法速度差不多。
最后我附一下两种的代码。
第一种:

//洛谷4195 
#include <bits/stdc++.h>
using namespace std;

long long a,b,p,x;
map<long long,long long> mp;
inline long long gcd(long long x,long long y)
{
    return y?gcd(y,x%y):x;
}
inline long long ksm(long long x,long long y,long long mod)
{
    long long res=1;
    while(y)
    {
        if(y&1)
        res=(res*x)%mod;
        x=(x*x)%mod;
        y>>=1;
    }
    return res;
}
inline void exBSGS(long long a,long long b,long long p)
{
    if(b==1)
    {
        printf("0\n");
        return;
    }
    long long d=gcd(a,p),t=1,k=0;
    while(d!=1)
    {
        if(b%d)
        {
            printf("No Solution\n");
            return;
        }
        ++k;
        b/=d;
        p/=d;
        t=(t*(a/d))%p;//[1,k]的处理 
        d=gcd(a,p);
        if(b==t)
        {
            printf("%lld\n",k);
            return;
        }
    }
    mp.clear();
    long long m=ceil(sqrt(p)),ans;
    for(int j=0;j<=m;++j)
    {
        if(j==0)
        {
            ans=b%p;
            mp[ans]=j;
            continue;
        }
        ans=(ans*a)%p;
        mp[ans]=j;
    }
    long long x=ksm(a,m,p),pd=0;
    ans=t;
    for(int i=1;i<=m;++i)
    {
        ans=(ans*x)%p;
        if(mp[ans])
        {
            x=i*m-mp[ans];
            printf("%lld\n",x+k);
            pd=1;
            break;
        }
    }
    if(!pd)
    printf("No Solution\n");
    return;
}
int main()
{
    while(~scanf("%lld%lld%lld",&a,&p,&b))
    {
        if(a==0&&b==0&&p==0)
        break;
        a=a%p;
        b=b%p;
        exBSGS(a,b,p);
    }
    return 0;
}

第二种:

#include <bits/stdc++.h>
using namespace std;

long long a,b,p,x;
map<long long,long long> mp;
inline long long gcd(long long x,long long y)
{
    return y?gcd(y,x%y):x;
}
inline long long ksm(long long x,long long y,long long mod)
{
    long long res=1;
    while(y)
    {
        if(y&1)
        res=(res*x)%mod;
        x=(x*x)%mod;
        y>>=1;
    }
    return res;
}
void exgcd(long long a,long long b,long long &x,long long &y)
{
    if(!b)
    {
        x=1;
        y=0;
    }
    else
    {
        exgcd(b,a%b,y,x);
        y-=a/b*x;
    }
}
inline void exBSGS(long long a,long long b,long long p)
{
    if(b==1)
    {
        printf("0\n");
        return;
    }
    long long d=gcd(a,p),t=1,k=0;
    while(d!=1)
    {
        if(b%d)
        {
            printf("No Solution\n");
            return;
        }
        ++k;
        b/=d;
        p/=d;
        t=(t*(a/d))%p;//[1,k]的处理 
        d=gcd(a,p);
        if(b==t)
        {
            printf("%lld\n",k);
            return;
        }
    }
    mp.clear();
    long long m=ceil(sqrt(p)),ans,x,y;
    exgcd(t,p,x,y);
    x=(x%p+p)%p;
    if(!x)
    x+=p;
    b=b*x;
    for(int j=0;j<=m;++j)
    {
        if(j==0)
        {
            ans=b%p;
            mp[ans]=j;
            continue;
        }
        ans=(ans*a)%p;
        mp[ans]=j;
    }
    long long pd=0;
    ans=1;
    x=ksm(a,m,p);  
    for(int i=1;i<=m;++i)
    {
        ans=(ans*x)%p;
        if(mp[ans])
        {
            x=i*m-mp[ans];
            printf("%lld\n",x+k);
            pd=1;
            break;
        }
    }
    if(!pd)
    printf("No Solution\n");
    return;
}
int main()
{
    while(~scanf("%lld%lld%lld",&a,&p,&b))
    {
        if(a==0&&b==0&&p==0)
        break;
        a=a%p;
        b=b%p;
        exBSGS(a,b,p);
    }
    return 0;
}

PS:由于我自带大常数,所以在洛谷上不开O2几乎要T了

猜你喜欢

转载自blog.csdn.net/forever_shi/article/details/80558799