【数学 BSGS】bzoj2242: [SDOI2011]计算器
数论的板子集合……
Description
你被要求设计一个计算器完成以下三项任务:
1、给定y,z,p,计算Y^Z Mod P 的值;
2、给定y,z,p,计算满足xy≡ Z ( mod P )的最小非负整数;
3、给定y,z,p,计算满足Y^x ≡ Z ( mod P)的最小非负整数。
Input
输入包含多组数据。
第一行包含两个正整数T,K分别表示数据组数和询问类型(对于一个测试点内的所有数据,询问类型相同)。
以下行每行包含三个正整数y,z,p,描述一个询问。
Output
对于每个询问,输出一行答案。对于询问类型2和3,如果不存在满足条件的,则输出“Orz, I cannot find x!”,注意逗号与“I”之间有一个空格。
题目分析
第一个问题:快速幂解决
第二个问题:1.转为exgcd问题 2.直接$x=Z*y^{-1}$
第三个问题:BSGS
BSGS是引入分块的思想解决形如$A^x≡B(mod\,C) C为素数$的问题(至于C不是素数就是exBSGS的范畴了)
具体来说,就是记$size=\sqrt C$,$x=i*size-j \, (0≤j<\sqrt C)$,于是式子就成了$A^{i*size}≡A^j*B$的形式。而右边这个东西是可以预处理出来放在hash表里的,这样在$\sqrt C$枚举$i$的过程中,就可以$O(1)/O(log \, n)$判断是否有相应的j了。
这类思想挺妙的,应该可以迁移到其他地方。
1 #include<bits/stdc++.h> 2 typedef long long ll; 3 4 int T,k; 5 ll x,y,p,w,z,d,cir; 6 std::map<ll, int> mp; 7 8 ll qmi(ll a, ll b) 9 { 10 ll ret = 1; 11 for (a%=p; b; b>>=1, a=a*a%p) 12 if (b&1) ret = ret*a%p; 13 return ret; 14 } 15 ll gcd(ll a, ll b){return !b?a:gcd(b, a%b);} 16 void exgcd(ll a, ll b, ll &x, ll &y) 17 { 18 if (!b){ 19 x = 1, y = 0; 20 return; 21 } 22 exgcd(b, a%b, y, x), y -= a/b*x; 23 } 24 ll BSGS(ll a, ll b, ll p) 25 { 26 if (((!b)&&(!a))) return 1; 27 if ((!a)&&b) return -1; 28 if (b==1) return 0; 29 ll size = ceil(sqrt(p)), bse = 1; 30 mp.clear(); 31 for (int i=0; i<size; i++) 32 { 33 mp[bse*b%p] = i; 34 bse = bse*a%p; 35 } 36 for (ll i=0, now=1; i<=p; i+=size, now = now*bse%p) 37 if (mp.count(now)) return ((i-mp[now])%p+p)%p; 38 return -1; 39 } 40 int main() 41 { 42 for (scanf("%d%d",&T,&k); T; --T) 43 { 44 scanf("%lld%lld%lld",&x,&y,&p); 45 if (k==1) printf("%lld\n",qmi(x, y)); 46 if (k==2){ 47 x %= p, y %= p; 48 if (!x) puts("Orz, I cannot find x!"); 49 else printf("%lld\n",y*qmi(x, p-2)%p); 50 } 51 if (k==3){ 52 x %= p, y %= p, d = BSGS(x, y, p); 53 if (d==-1) puts("Orz, I cannot find x!"); 54 else printf("%lld\n",d); 55 } 56 } 57 return 0; 58 }
END