【模板】扩展BSGS板子 POJ3243 Clever Y
鉴于CQOI2018居然考了一道BSGS的板子题,就默默得又复习了一下BSGS以及又学了EXBSGS,CQOI那道题太板了,朴素BSGS,可以直接参考我以前的文章:POJ2417高次同余方程
这个HASH表的模板是直接抄的,,不会啊,,看来下一步是有必要学一下HASH表了。
EXBSGS的不同于朴素BSGS的地方是,EXBSGS的模数可以不受到一定要是质数的影响。可以是任何数。
令 d = gcd(A, C) , A = ad, B = bd, C = cd
则 ad ≡ bd (mod cd)
等价于 a ≡ b (mod c )
问题变成了求D * A^(x-cnt) ≡ B (mod C),因此我们先消除因子,不断地枚举r=gcd(a,c),然后b/=r,c/=r,base*=(a/r)(base是指的是大小步算法里面的大步块),然后将令x = i * m + j + cnt,cnt为消除的次数,方法就和朴素BSGS一样了。但是我们要考虑其x小于cnt的情况,于是,我们先要枚举cnt次,大约(log C)次,判断得到的是不是刚好等于B。
以下是code....
#include<map> #include<cstdio> #include<iostream> #include<cmath> typedef long long ll; using namespace std; ll gcd(ll a,ll b) { return !b?a:gcd(b,a%b); } struct Hashmap { static const int Ha=999917,maxe=46340; int E,lnk[Ha],son[maxe+5],nxt[maxe+5],w[maxe+5]; int top,stk[maxe+5]; void clear() {E=0;while (top) lnk[stk[top--]]=0;} void Add(int x,int y) {son[++E]=y;nxt[E]=lnk[x];w[E]=INT_MAX;lnk[x]=E;} bool count(int y) { int x=y%Ha; for (int j=lnk[x];j;j=nxt[j]) if (y==son[j]) return true; return false; } int& operator [] (int y) { int x=y%Ha; for (int j=lnk[x];j;j=nxt[j]) if (y==son[j]) return w[j]; Add(x,y);stk[++top]=x;return w[E]; } }; Hashmap ma; ll exgcd(ll a,ll b,ll &x,ll &y) { if(!b) { x=1; y=0; return a; } ll r=exgcd(b,a%b,x,y); ll tmp=x; x=y; y=tmp-(a/b)*y; return r; } ll EXbsgs(ll a,ll b,ll c) { if(c==1) {if(!b) return a!=1; else return -1;} if(b==1) {if(a) return 0; else return -1;} if(a%c==0) {if(!b) return 1; else return -1;} ll num=0,r,d=1; while( (r=gcd(a,c))>1 ) { if(b%r) return -1; ++num; b/=r; c/=r; d=d*a/r%c; } for(ll i=0,now=1;i<num;i++,now=now*a%c) if(now==b) return i; ll bb=1; ma.clear(); ll m=ceil(pow(c,0.5)); for(ll i=0;i<m;i++) { if(!ma.count(bb)) ma[bb]=i; bb=bb*a%c; } ll x,y; for(ll i=0;i<m;i++) { r=exgcd(d,c,x,y); x=(x*b%c+c)%c; if(ma.count(x)) return i*m+ma[x]+num; d=d*bb%c; } return -1; } int main() { ll a,b,c,ans; while(233) { scanf("%lld%lld%lld",&a,&c,&b); if(a+b+c==0) return 0; ans=EXbsgs(a,b,c); ans==-1?printf("No Solution\n"):printf("%lld\n",ans); } }