这题也是神坑之一···
细节很多···
没开long long 调了我一个小时气死爸爸哦
既然这样先来说一下大步小步
大步小步算法是专门用来求解a^x = b(mod n)这种方程的,这种问题也称为离散对数问题。
这个在之前gzz讲数学的笔记里有详细提到
A^x=B (mod C) (A,C)=1
知A,B,C 求最小非负x
设X=x1+x2
则a^x1*a^x2=b mod c
O(sqrt(phi(c))) 分成sqrt(phi(C))段
哈希建表表示A^x->x的映射,将0~sqrt(phi(c))放进去
X=k*sqrt(phi(c))+x2
A^x2=B*1/a^phi(c),在哈希表里找到对应的x2
这里求个逆元就好啦
枚举k*sqrt(phi(c))求x2
如果在根号phi(c)内有更小的循环节,则求出来的值要最小的
这个必须在a,c互质的情况下
那么不互质的情况
就是扩展大步小步
其实这个跟大步小步没有多大关系···
既然不互质就有gcd
那么我们不断把gcd提出来
直到a,p互质
再用大步小步思想求出
并且记录提出gcd的次数num
在0-num暴力算下有没有符合的情况
嗯注释给的很明晰了:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<map>
#define int long long
using namespace std;
int a,b,p;
map<int,int> mp;
inline int rd(){
int x=0,f=1; char c=' ';
while(c<'0' || c>'9') {if(c=='-') f=-1;c=getchar();}
while(c<='9' && c>='0') x=x*10+c-'0',c=getchar();
return x*f;
}
int gcd(int x,int y){
if(y==0) return x;
return gcd(y,x%y);
}
int exgcd(int a,int b,int &x,int &y){
if(b==0) {x=1,y=0; return a;}
int d=exgcd(b,a%b,x,y);
int z=x;
x=y;
y=z-a/b*y;//这个地方写成了y*a/b调了一小时···千万别这样写啊
return d;
}
int inv(int a,int b){
int x,y;
exgcd(a,b,x,y);
return (x%b+b)%b;
}
int qpow(int x,int k,int mod){
int ret=1;
while(k){
if(k&1) ret=(ret*x)%mod;
x=(x*x)%mod;
k>>=1;
}
return ret;
}
int phi(int n){
int res=n;
int t=sqrt(n);
for(int i=2;i<=t;i++){
if(n%i==0){
res=(res/i)*(i-1);
while(n%i==0) n/=i;
}
}
if(n>1) res=(res/n)*(n-1);
return res;
}
int solve(){
int d;
int aa=a,bb=b,pp=p;
int x=1,num=0;
while((d=gcd(a,p))!=1 && b%d==0){//同除gcd,但a不能直接除,用x记录除去的部分
x*=a/d; b/=d; p/=d;
num++; x%=p;
}
b=b*inv(x,p)%p;
if(b==1) return num;
if(b%gcd(a,p)) return -1;//如果b不能整除gcd(a,p)则无解
int last=1;
for(int i=0;i<=num;(last*=a)%=pp,i++){
if(last==bb) return i;
}//暴力枚举0~num之间是否存在k,存在则为最小的
last=1;
int now=phi(p);
int bl=sqrt(now);
for(int i=0;i<=bl;(last*=a)%=p,i++){//哈希建表
int q=last;
if(!mp.count(q)) mp[q]=i;
}
last=1;
int k=qpow(a,bl,p);
for(int i=0;i<=bl;(last*=k)%=p,i++){
int q=b*inv(last,p)%p;//同常规BSGS一样
if(mp.count(q))
return i*bl+num+mp[q];
}
return -1;
}
signed main(){
while(1){
a=rd(); p=rd(); b=rd();
if(a==0 && p==0 && b==0) break;
b%=p;
int k=solve();
if(k==-1) printf("No Solution\n");
else printf("%d\n",k);
mp.clear();//记得清空map
}
}