BSGS入门
Posted yuyanjiab
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了BSGS入门相关的知识,希望对你有一定的参考价值。
做了这么长时间数论 应该整合一下
在mod意义下 我们能完成的运算:
加
减(mod m + m mod m)
快速幂 快速乘
逆元(除) 有有解的条件
开方? 这个设计原根的知识 下一篇讲
然后 就是取对数了 也就是著名的 离散对数 问题
(话说连续对数还不太熟练呢.....)
Question: 给定方程 a ^ x ≡ b (mod c) 求x
Solution:
首先可以容斥地发现 如果有解 0~c范围内一定有一个解
所以枚举! O(c)即可通过本题!
End.
(啪!)
大部分情况下模数都是1e9+7这样的东西 所以枚举显然是过不去的
由于连续情况下 我们并不能高效的得出某一个对数的具体值
所以枚举的思路依然要保留
我们发现其实上面的算法有一个很小的思路优化 : 枚举0~c-1的每个数
也就是说 这里面的每个数 都代表了一类数
(虽然并不是剩余系)
所以我们考虑 能不能先算出一部分答案 然后利用这些答案去简化别的答案的运算
于是! Baby-step giant-step 思想就诞生了
Baby_step,giant_step,意为先小步,后大步,这是一个很神奇的想法。
能够降低枚举的规模从 n 到 sqrt(n),怎么实现的呢,我们看下面。
sqrt(n) -> 考虑分块 (记块大小为B)
然后我们发现 每个块的大小恒定 所以可以拆分成i=x*B+y的形式
我们先O(sqrt(n)) 处理0~B-1的答案
如果没有找到就存HASH表里
然后O(sqrt(n)) 遍历所有块同时O(1)查找有没有答案
然后就OK
但是有一个问题
这里必须保证把a^i除到b那边以后没有问题
也就是a 有 mod c 的逆元
所以我们可以一直求gcd(a,c) 然后除掉并且每次拿一个a出来(++tot)
最后答案加上tot就OK
Code:
1 #include<cstdio> 2 #include<cstring> 3 #include<cmath> 4 #include<algorithm> 5 #define ms(a,b) memset(a,b,sizeof a) 6 #define inf 2147483647 7 #define itn int 8 #define rep(i,a,n) for(itn i = a;i <= n;i++) 9 #define per(i,n,a) for(itn i = n;i >= a;i--) 10 #define eps 1e-8 11 using namespace std; 12 typedef long long ll; 13 const int N = 50005; 14 //head 15 int X,Z,K; 16 ll gcd(ll a,ll b){return b ? gcd(b,a%b) : a;} 17 ll exgcd(ll a,ll b,ll &x,ll &y) { 18 if(!b) { 19 x = 1,y = 0; 20 return a; 21 } 22 ll t = exgcd(b,a%b,y,x); 23 y -= (a/b) * x; 24 return t; 25 } 26 27 ll inv(ll a,ll p) { 28 ll x,y; 29 exgcd(a,p,x,y); 30 return (x%p+p)%p; 31 } 32 33 struct Hash { 34 const static int H = 999979; 35 int head[H],nxt[N],num[N],val[N],cnt; 36 void init() { 37 cnt = 0; 38 ms(head,0),ms(nxt,0),ms(val,0); 39 } 40 void ins(int x,int y) { 41 int h = x % H; 42 for(int i = head[h];i;i = nxt[i]) { 43 if(num[i] == x) {val[i] = y;return;} 44 } 45 nxt[++cnt] = head[h]; 46 head[h] = cnt; 47 num[cnt] = x,val[cnt] = y; 48 } 49 int qry(int x) { 50 int h = x % H; 51 for(int i = head[h];i;i = nxt[i]) { 52 if(num[i] == x) return val[i]; 53 } 54 return -1; 55 } 56 }HASH; 57 58 //a ^ x == b(mod p) 59 int BSGS(int a,int b,int p) { 60 // gcd(a,p) <- 1 61 int tot = 0,G; 62 int d = 1; 63 while( (G = gcd(a,p)) ^ 1 ) { 64 if(b%G) return -1; 65 tot++; 66 b /= G,p /= G; 67 d = (ll)d * (a/G) % p; 68 } 69 b = (ll)b * inv(d,p) % p; 70 71 // ins(a^(0~B-1)) 72 HASH.init(); 73 int B = sqrt(p); 74 int x = 1; 75 rep(i,0,B-1) { 76 if(x == b) return i + tot; 77 HASH.ins((ll)x * b % p,i); 78 x = (ll)x * a % p; 79 } 80 81 //a^im = b * a^(-y) 82 //O(B) + O(hash) 83 int q = x; 84 for(int i = B;i - (B-1) <= p-1;i += B) { 85 int tmp = HASH.qry(q); 86 if(tmp != -1) return i - tmp + tot; 87 q = (ll)q * x % p; 88 } 89 return -1; 90 } 91 92 bool spj() { 93 if(X == 0) { 94 puts("No Solution"); 95 return 1; 96 } 97 int res = 1; 98 rep(i,0,10) { 99 if(res == K) { 100 printf("%d ",i); 101 return 1; 102 } 103 res = (ll)res * X % Z; 104 } 105 return 0; 106 } 107 108 //x ^ y == k(mod z) 109 main() { 110 while(~scanf("%d%d%d",&X,&Z,&K)) { 111 if(!X && !Z && !K) return 0; 112 X %= Z;K %= Z; 113 if(spj()) continue; 114 int ans = BSGS(X,K,Z); 115 if(ans == -1) puts("No Solution"); 116 else printf("%d ",ans); 117 } 118 }
以上是关于BSGS入门的主要内容,如果未能解决你的问题,请参考以下文章
BZOJ 3239 Discrete Logging(BSGS)