1. 程式人生 > >BSGS入門

BSGS入門

ace 枚舉 display cstring 逆元 urn 數論 scan ....

做了這麽長時間數論 應該整合一下

在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\n",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\n",ans); 117 } 118 }
View Code

BSGS入門