/************************************* ---高次同余方程模板BabyStep-GiantStep--- 输入:对于方程A^x=B(mod C),调用BabyStep(A,B,C),(0<=A,B,C<=10^9) 输出:无解放回-1,有解放回最小非负整数x 复杂度:O(C^0.5),只与C有关,与A,B的大小无关 ************************************/ typedef long long ll; #define HASH_N 100007 struct hashnode { int next; ll key; int j; }HashLink[ HASH_N ]; int hashpre[ HASH_N ],hashcnt; void hash_insert(ll x,ll key,int j) { for(int p=hashpre[x];p!=-1;p=HashLink[p].next) { if(HashLink[p].key==key) return ; } HashLink[ hashcnt ].key = key; HashLink[ hashcnt ].j = j; HashLink[ hashcnt ].next = hashpre[x]; hashpre[x] = hashcnt++; } int hash_get(ll key) { ll x=key%HASH_N; for(int p=hashpre[x];p!=-1;p=HashLink[p].next) { if( HashLink[p].key == key ) return HashLink[p].j; } return -1; } ll gcd(ll a,ll b) { if(b==0) return a; return gcd(b,a%b); } //ax + by = gcd(a,b) //传入固定值a,b.放回 d=gcd(a,b), x , y void extendgcd(long long a,long long b,long long &d,long long &x,long long &y) { if(b==0){d=a;x=1;y=0;return;} extendgcd(b,a%b,d,y,x); y-=x*(a/b); } //Ax=1(mod M) //返回x的范围是[0,M-1] ll GetNi(ll A,ll M) { ll rex=0,rey=0; ll td=0; extendgcd(A,M,td,rex,rey); return (rex%M+M)%M; } //a^b%mod 快速幂 long long Quk_Mul(long long a,long long b,long long mod) { long long qsum=1; while(b) { if(b&1) qsum=(qsum*a)%mod; b>>=1; a=(a*a)%mod; } return qsum; } //测试x较小的情况,必须! ll firsttest(ll A,ll B,ll C) { ll tmp=1; if(B==1) return 0; for(int i=1;i<100;i++) { tmp = (tmp*A)%C; if(tmp==B) return i; } return -1; } //欧拉函数 返回[1,x-1]中与x互素的数的个数,复杂度n^0.5 ll euler(ll x) { ll i, res=x; for (i = 2; i < (ll)sqrt(x * 1.0) + 1; i++) if(x%i==0) { res = res / i * (i - 1); while (x % i == 0) x /= i; } if (x > 1) res = res / x * (x - 1); return res; } ll BabyStep(ll A,ll B,ll C) { if(0==A || 0==C) return -1; if(C==1) return 0; B = B%C; ll ans = firsttest(A,B,C);//为了防止x比较小的时候 if(ans != -1) return ans; ll D=1; int c=0; ll d; while( (d=gcd(A,C)) != 1 ) { if( B%d !=0 ) return -1;//无解的情况 B /= d; C /= d; D = D*A/d%C; c++; } //得到了 D*A^(x-c)=B (mod C) ,gcd(A,C)=1 , gcd(D,C)=1 ll D_1=GetNi(D,C);//求D的逆元 B = B*D_1%C; //求A^x=B (mod C),然后返回x+c ll m = ceil( sqrt(C+0.0) ); memset(hashpre,-1,sizeof(hashpre)); hashcnt=0; ll hashnum=1; hash_insert(1, 1, 0); for(int i=1;i<m;i++) { hashnum = (hashnum*A)%C; hash_insert(hashnum%HASH_N, hashnum ,i); } ll ol=euler(C); ll mA=Quk_Mul(A, m, C); ll ta=1; ll tmpni = Quk_Mul(mA, ol-1, C); for(int i=0;i<m;i++,ta=ta*tmpni%C) { //解线性同余方程 tx=B*ta (%C) ,ta直接用费马小定理求逆元 ll tx = ta; tx = (tx*B)%C; int j=hash_get(tx); if(j!=-1)//找到解了 { return i*m+j+c; } } return -1; }
其实还是很很正常的解法,这个甚至可以当成一道难一点的数论题目做。
关于详细的解释。
时间: 2024-12-27 10:29:39