#费马小定理,BSGS#BZOJ 3285 离散对数解指数方程
题目
求最小的正整数 \(x\) 满足 \(g^{ax+b}\equiv c\pmod p\)
其中 \(p\) 是一个质数, \(g,a,b,c\leq 10^{1000000},p\leq 2^{31}\)
分析
先将 \(g^a\)和 \(g^b\) 用费马小定理求出来,
问题就转换成 \(b'\times {a'}^x\equiv c'\pmod p\)
这个直接套用 BSGS 就可以了
代码
#include <cstdio>
#include <cctype>
#include <cmath>
#include <cstring>
#define rr register
using namespace std;
const int p=70001; typedef long long lll;
const int N=1000011; char A[N],B[N],C[N],G[N];
lll mod,lA,lB,lC,ans,lG,a,b,c,g;
struct Linked_Hash{
struct node{int y,w,next;}E[p]; int Et,hs[p];
inline void Clear(){Et=0,memset(hs,-1,sizeof(hs));}
inline void Insert(int w,int x){E[++Et]=(node){x,w,hs[w%p]},hs[w%p]=Et;}
inline signed locate(int W){
for (rr int i=hs[W%p];~i;i=E[i].next)
if (E[i].w==W) return E[i].y;
return -1;
}
}ha;
inline lll gcd(lll x,lll y){return y?gcd(y,x%y):x;}
inline lll exBSGS(lll a,lll c,lll b,lll mod){
ha.Clear();
rr lll GCD=gcd(a,mod),t=1;
rr lll CNT=0,ir=sqrt(mod)+1;
while (GCD>1){
if (b%GCD||c%GCD) return -1;
b/=GCD,c/=GCD,mod/=GCD,
t=t*(a/GCD)%mod,
GCD=gcd(a,mod),++CNT;
if (b==t*c%mod) return CNT;
}
rr lll now=1;
for (rr int i=0;i<ir;++i)
ha.Insert(now*b%mod,i),now=now*a%mod;
a=now,now=t;
if (!a) return !b?1:-1;
for (rr int i=0;i<=ir;++i){
rr int j=ha.locate(now*c%mod);
if (j>=0&&i*ir+CNT>j) return i*ir+CNT-j;
now=now*a%mod;
}
return -1;
}
inline lll ksm(lll x,lll y){
rr lll ans=1;
for (;y;y>>=1,x=x*x%mod)
if (y&1) ans=ans*x%mod;
return ans;
}
signed main(){
scanf("%s%s%s%s%lld",A+1,B+1,C+1,G+1,&mod);
lA=strlen(A+1),lB=strlen(B+1),lC=strlen(C+1),lG=strlen(G+1);
for (rr int i=1;i<=lG;++i) g=(g*10+G[i]-48)%mod;
for (rr int i=1;i<=lC;++i) c=(c*10+C[i]-48)%mod;
for (rr int i=1;i<=lA;++i) a=(a*10+A[i]-48)%(mod-1);
for (rr int i=1;i<=lB;++i) b=(b*10+B[i]-48)%(mod-1);
a=ksm(g,a),b=ksm(g,b),ans=exBSGS(a,b,c,mod);
if (ans==-1) printf("no solution");
else printf("%lld",ans);
return 0;
}