BSGS算法主要解决 :求a^x = b ( mod p ) 的最小满足题意的正整数 x 的问题。
主要的解决方法是:
将 x 分成 i * m - j,所以就变成了 a ^( i * m - j ) = b ( mod p )
m = ceil ( sqrt ( p ) )
变形之后即为 ( a ^ m ) ^ i = b * a ^ j ( mod p )。
在(0 - m )范围内存下b * a ^ j 的值。
再在 (1 - m )范围枚举,直到( a ^ m ) ^ i 存在与之配对的值,此时 x 可求。
具体证明:
因为 x = i*m-j , 所以x 的最大值不会超过p
a(k mod p-1) = ak (mod p) 证明这个公式,(需要用到费马小定理)
k mod p-1 就是 k-m(p-1) ,原式就变成了 ak-m(p-1) ≡ ak (mod p)
再变一步 ak / am(p-1) ≡ ak (mod p)
这时让 am(p-1) ≡ 1 (mod p) 就行了。
由费马小定理知: 当p为质数且 (a,p) = 1 时 ap-1 ≡ 1 (mod p)
所以推出 p 为质数 且 (a,p)=1 这个条件, 所以 a(k mod p-1) ≡ a k (mod p)
所以:如果枚举 x 的话枚举到 p 即可。
所以使 im−j<=p , 即 m=⌈√p⌉ , i,j 最大值也为m。
感谢 mjtcn
代码如下:
1 #include<cstdio>
2 #include<cmath>
3 #include<algorithm>
4 #include<map>
5 using namespace std;
6 typedef long long ll;
7 const int maxn = 1e7 + 7;
8 ll read()
9 {
10 ll a = 0, b = 1;
11 char c = getchar();
12 while(c < '0' or c > '9')
13 {
14 if(c =='-') b = -1;
15 c = getchar();
16 }
17 while(c >= '0' and c <= '9')
18 {
19 a = a * 10 + c - '0';
20 c = getchar();
21 }
22 return a * b;
23 }
24 map <ll, int>t;
25 ll n,b,p;
26 ll quickpow(ll a, ll b)
27 {
28 ll ans = 1;
29 ll base = a;
30 while( b != 0)
31 {
32 if(b %2 == 1)
33 ans *= base;
34 b /= 2;
35 base*=base;
36 ans %= p;
37 base %= p;
38 }
39 return ans;
40 }
41 int main()
42 {
43 p = read(); b = read(); n = read();
44 //printf("%lld",quickpow(n,b));
45 /* for(int i=1; quickpow(b,i) < maxn; i++)
46 {
47 t[quickpow(b,i)] = i;
48 }
49 for(int i=1; i * p + n < maxn; i++)
50 {
51 if(t[i*p + n])
52 printf("%lld",t[i*p + n]);
53 break;
54 }*/
55 ll m = ceil(sqrt(p));
56 if(b % p == 0)
57 {
58 printf("no solution");
59 return 0;
60 }
61 ll ret , k = 0 ;
62 ret = n % p;
63 t[ret] = 0;
64 for(int i=1; i<=m; i++)
65 {
66 ret = (ret * b) % p;
67 t[ret] = i;
68 }
69 ret = quickpow(b,m);
70 ll now = 1;
71 for(int i=1; i<=m; i++)
72 {
73 now = (now * ret) % p;
74 if(t[now])
75 {
76 ll ans = i * m - t[now];
77 printf("%lld",(ans % p + p) % p);
78 k = 1;
79 break;
80 }
81 }
82 if(k == 0)
83 printf("no solution");
84 return 0;
85 }