2480: Spoj3105 Mod
Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 797 Solved: 273
[ Submit][ Status][ Discuss]
Description
已知数a,p,b,求满足a^x≡b(mod p)的最小自然数x。
Input
每个测试文件中最多包含100组测试数据。
每组数据中,每行包含3个正整数a,p,b。
当a=p=b=0时,表示测试数据读入完全。
Output
对于每组数据,输出一行。
如果无解,输出“No Solution”(不含引号),否则输出最小自然数解。
Sample Input
5 58 33
2 4 3
0 0 0
2 4 3
0 0 0
Sample Output
9
No Solution
No Solution
HINT
100%的数据,a,p,b≤1e9。
2016.3.29新加数据一组 by 1430586275
2016.3.29新加数据一组 by 1430586275
Source
思考
扩展BSGS
作用
BSGS只能处理C是素数的情况,太特殊了,应用范围不大。而扩展BSGS可以解决C不是素数的一般情况。
方法
设d=gcd(A,C),那么A,B,C可以表示为A=a*d,B=b*d(如果B不是d的倍数且B!=1则显然无解),C=c*d。也就是说(a∗d)x≡b∗d(mod。
根据同余性质,这个式子可以同除d,得到a∗(a∗d)x−1≡b(mod),D*=A/d,num++,直到d=1为止。
最后方程就变成这种形式了:D∗Ax−num≡B(mod,所以x将会>=num,这就导致0~num-1枚举不到。所以要在最开始枚举一下0~num-1,这个问题就解决了。
学习一下ex_gcd的灵活运用,吐槽因为有了ex_gcd导致我们还要学习扩展CRT与扩展BSGS…………
就是与扩展CRT几乎一样的思路,将非质数拆分后处理。
//1467与2480的双倍经验,修改一下输入输出就可以过3239
#include<map>
#include<cmath>
#include<cstdio>
#include<iostream>
using namespace std;
map<long long,long long> hash;
long long gcd(long long a,long long b){return b==0?a:gcd(b,a%b);}
void exgcd(long long a,long long b,long long &x,long long &y){
if(!b) {x=1;y=0;return;}
exgcd(b,a%b,y,x);y-=x*(a/b);
}
long long inv(long long a,long long p){if(!a) return 1;long long x,y;exgcd(a,p,x,y);return (x%p+p)%p;}
long long pow(long long a,long long b,long long p){
long long ret=1;
while(b){
if(b&1)ret=ret*a%p;
a=a*a%p;b>>=1;
}
return ret;
}
long long BSGS(long long a,long long b,long long p){
if(a==0)return b==0?1:-1;
b%=p;a%=p;
hash.clear();
long long cnt=0;
for(int g=gcd(a,p);g!=1;g=gcd(a,p)){
if(b%g) return -1;
b/=g;p/=g;b=b*inv(a/g,p)%p;
cnt++;
if(b==1)return cnt;
}
if(p==1)return cnt;
int m=(int)sqrt(p)+1;
long long e=1;hash[e]=0;
long long v=inv(pow(a,m,p),p);
for(int i=1;i<m;i++){
e=e*a%p;
if(!hash.count(e))hash[e]=i;
}
for(int i=0;i<=m;i++){
if(hash.count(b))return i*m+hash[b]+cnt;
b=b*v%p;
}
return -1;
}
long long a,b,p;
int main(){
while(cin>>a>>p>>b&&p){
long long t=BSGS(a,b,p);
if(t==-1)puts("No Solution");
else cout<<t<<endl;
}
return 0;
}