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 }
View Code

 

以上是关于BSGS入门的主要内容,如果未能解决你的问题,请参考以下文章

推荐net开发cad入门阅读代码片段

bsgs整理

BZOJ 3239 Discrete Logging(BSGS)

BSGS&EXBSGS 大手拉小手,大步小步走

51Nod1039 N^3 Mod P 数论 原根 BSGS

BSGS及扩展BSGS模板