BSGS算法 || POJ2417 || luogu P3846
吐了。。。写完poj2417之后意识到poj不支持stl和__int128。。。你好歹是个大学的软件,不管你们acmer的吗。。
鬼才写快速乘和hash。不让我用__int128和map我就不交poj上了。发现luogu有一模一样的板子题。
题目:P3846 [TJOI2007] 可爱的质数/【模板】BSGS
题意:
讲一下bsgs算法。bsgs算法的思路是,既然答案要求L,设m为sqrt(P),则可将L表示为km-j(其中0<=k,j<=m,且 km-j>=0 )。
(由费马小定理可知若存在L,L必小于P。)则原式转化为Bkm=N×Bj。原问题转化为找出该方程的一组解,并且使得L=km-j最小。
大学生(可以公然使用STL的人上人)的做法是:预处理出N×Bj(mod P)的值,并且用map记录此时的 j 值。然后从0到m枚举k,
只要满足上式且km-j>=0即可的出答案。注意bsgs不能处理余数为0的情况。
由于虽然对P取模,但是两个long long相乘仍可能炸long long,所以大部分bsgs题目需要一些处理。苦逼高中生的做法是快速乘。
快乐大学生的做法是直接用(__int128)暂时存储相乘后的结果,取模后再强制转化回long long类型。
其实可以不用快速幂,luogu上测了199ms;用了快速幂后是366ms。因为单次快速幂是log的,本来很快,但是bsgs问题P太大了。
bsgs算法时间复杂度约为O(sqrt(P))。
代码:
不使用快速幂:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<bits/stdc++.h> 2 #define ll long long 3 using namespace std; 4 ll p,b,n; 5 map<ll,int>mp; 6 /*inline ll ksm(ll a,ll b,ll p){ 7 ll res=1; 8 while(b){ 9 if(b&1)res=(__int128)res*a%p; 10 a=(__int128)a*a%p; 11 b>>=1; 12 } 13 return res; 14 }*/ 15 ll bsgs(ll p,ll b,ll n){ 16 mp.clear(); 17 ll m=pow(p,0.5)+1,cf=1; 18 for(int i=0;i<=m;i++){ 19 if(i!=0) cf=(__int128)cf*b%p; 20 else cf=1; 21 mp[(__int128)cf*n%p]=i; 22 } 23 b=cf; 24 for(int i=0;i<=m;i++){ 25 if(i) cf=(__int128)cf*b%p;else cf=1; 26 if(mp.find(cf)!=mp.end()){ 27 int j=mp[cf]; 28 if(i*m-j>=0) return i*m-j; 29 } 30 } 31 return -1; 32 } 33 int main(){ 34 while(~scanf("%lld%lld%lld",&p,&b,&n)){ 35 if(n!=0){ 36 ll ans=bsgs(p,b,n); 37 if(ans!=-1) printf("%lld\n",ans); 38 else puts("no solution"); 39 } 40 // bsgs 不能处理整除的情况。但是这道题不涉及。 41 } 42 return 0; 43 }
使用快速幂:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<bits/stdc++.h> 2 #define ll long long 3 using namespace std; 4 ll p,b,n; 5 map<ll,int>mp; 6 inline ll ksm(ll a,ll b,ll p){ 7 ll res=1; 8 while(b){ 9 if(b&1)res=(__int128)res*a%p; 10 a=(__int128)a*a%p; 11 b>>=1; 12 } 13 return res; 14 } 15 ll bsgs(ll p,ll b,ll n){ 16 mp.clear(); 17 ll m=pow(p,0.5)+1,cf=1; 18 for(int i=0;i<=m;i++){ 19 cf=ksm(b,i,p); 20 mp[(__int128)cf*n%p]=i; 21 } 22 b=ksm(b,m,p); 23 for(int i=0;i<=m;i++){ 24 cf=ksm(b,i,p); 25 if(mp.find(cf)!=mp.end()){ 26 int j=mp[cf]; 27 if(i*m-j>=0) return i*m-j; 28 } 29 } 30 return -1; 31 } 32 int main(){ 33 while(~scanf("%lld%lld%lld",&p,&b,&n)){ 34 if(n!=0){ 35 ll ans=bsgs(p,b,n); 36 if(ans!=-1) printf("%lld\n",ans); 37 else puts("no solution"); 38 } 39 // bsgs 不能处理整除的情况。但是这道题不涉及。 40 } 41 return 0; 42 }
By:AlenaNuna