求$a^x\equiv b(\mod p)$的最小整数解,其中p不一定为质数。

普通的BSGS只能解决a,p互质的问题,当a,p不互质怎么办呢?

扩展BSGS的思想:

每次取一个a,如果$\gcd(a,p)\not = 1$,则进行判定:如果$b\not | \gcd(a,p)$则无解,否则b和p全部$/\gcd(a,p)$。
取到$\gcd(a,p)=1$时结束,记录取的次数s。
取时记录d为每一次$\frac{a}{\gcd(a,p)}$的积。
取完以后b要$/ d$,也就是说新的b是原来的$b/a^s$

接着跑BSGS就行,计算出的答案要+取的次数。
这样就能算出一个$\ge s$的答案,但是答案如果$\le s$怎么办呢?

因为每次p最少$/2$,所以$s \le log_2{p}$,显然$s \le 50$,所以一开始先计算x在$[0,50]$之间有没有可能的解。

注意$b/d$时,计算逆元不能用费马小定理!要用扩欧!!!因为p不一定是个质数。
代码:(一开始做的是数据水p为质数的BZOJ1467)

/*
Author: CNYALI_LK
LANG: C++
PROG: 1467.cpp
Mail: cnyalilk@vip.qq.com
*/
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<map>
#define debug(...) fprintf(stderr,__VA_ARGS__)
#define DEBUG printf("Passing [%s] in LINE %lld\n",__FUNCTION__,__LINE__)
#define Debug debug("Passing [%s] in LINE %lld\n",__FUNCTION__,__LINE__)
#define all(x) x.begin(),x.end()
using namespace std;
const double eps=1e-8;
const double PI=acos(-1.0);
typedef long long ll;
template<class T>ll chkmin(T &a,T b){return a>b?a=b,1:0;}
template<class T>ll chkmax(T &a,T b){return a<b?a=b,1:0;}
template<class T>T sqr(T a){return a*a;}
template<class T>T mmin(T a,T b){return a<b?a:b;}
template<class T>T mmax(T a,T b){return a>b?a:b;}
template<class T>T aabs(T a){return a<0?-a:a;}
#define min mmin
#define max mmax
#define abs aabs
typedef pair<ll,ll> pii;
#define x first
#define y second
pii aa[2333333];
ll gcd(ll a,ll b){return b?gcd(b,a%b):a;}
ll find(ll l,ll r,ll s){
    ll mid;
    while(l<=r){
        mid=(l+r)>>1;
        if(aa[mid].x<=s)l=mid+1;
        else r=mid-1;
    }
    --l;
    if(aa[l].x==s)return aa[l].y;
    else return -1;
}
void exgcd(ll a,ll b,ll &x,ll &y){
    if(!b){x=1;y=0;return;}
    exgcd(b,a%b,y,x);
    y=y-(a/b)*x;
}
ll inv(ll a,ll p){
    ll x,y;
    exgcd(a,p,x,y);
    x=(x%p+p)%p;
    return x;
}
ll fpm(ll a,ll b,ll p){
    ll c=1;
    while(b){
        if(b&1)c=c*a%p;
        a=a*a%p;
        b>>=1;
    }
    return c;
}
ll exbsgs(ll a,ll p,ll b){
    b%=p;

    for(ll i=0,j=1%p;i<=50;++i,j=j*a%p){
        if(j==b)return i;
    }
    ll s=0,g,f=1%p;
    while((g=gcd(a,p))>1){
//      printf("!%lld\n",g);
        if(b%g){return -1;}
        b/=g;
        p/=g;
        f=f*a/g%p;
        ++s;
    }
    b=b*inv(f,p)%p;
    a%=p;

    ll m=ceil(sqrt((long double)p));
    for(ll i=1;i<=m;++i){
        b=b*a%p;
        aa[i]=make_pair(b,i);
    }
    sort(aa+1,aa+m+1);
//  for(ll i=1;i<=m;++i)printf("%lld%c",aa[i].x,i%10?' ':'\n');
    ll c=1,fs;
    for(ll i=1;i<=m;++i)c=c*a%p;
    b=1;
    for(ll i=1;i<=m;++i){
        b=b*c%p;
        ll ss=find(1,m,b);
        if(ss+1)return i*m-ss+s;
    }
    return -1;
}
int main(){
#ifdef cnyali_lk
    freopen("1467.in","r",stdin);
    freopen("1467.out","w",stdout);
#endif
    ll x,z,k,ans;
    while(cin>>x>>z>>k&&(x||z||k)){
        ans=exbsgs(x,z,k);
        if(ans+1)
        printf("%lld\n",ans);
        else printf("No Solution\n");
    }
    return 0;
}

标签: 数论

添加新评论