求方程:的解个数
分析:设,那么上述方程解的个数就与同余方程组:的解等价。
设同于方程的解分别是:,那么原方程的解的个数就是
所以现在的关键问题是求方程:的解个数。
这个方程我们需要分3类讨论:
第一种情况:
对于这种情况,如果方程的某个解设为,那么一定有,可以得到,即
所以方程的解个数就是:,也就是
第二种情况:
这样也就是说p|B,设,,本方程有解的充要条件是A|t,
那么我们设t=kA,
所以进一步有:,因为,这样又转化为第三种情况了。
第三种情况:
那么我们要求指标;求指标的话又要求原根。并且奇素数p的原根也是p^a的原根,所以说求个p的原根就好了。
且如果有解,则解的个数为(A,φ(p^a))。
求指标的话就是要解决A^x ≡ B (mod p^a)的问题。由于本情况保证了(p^a, B)=1,用个Baby-step-Giant-step就
能解决问题。
方程x^A ≡ B (mod p^a)有解,当且仅当(A,φ(p^a))|ind B。ind B表示B对于p^a的任一原根的指标。
如果不知道原根与指标的现在就补一下吧:
原根部分:
定义一:设m>1,(a,m)=1,则使得成立的最小正整数r,称为a对模m的指数,或者a对模m的阶,记为
定理一:若m>1,(a,m)=1,,则
定义二:若,则a是模m的原根。
定理二:如果大于1的正整数m有原根,那么它一共有个不同的原根。
定理三:模m有原根的必要条件是m=2,4,p^a或者2p^a,其中p是奇素数。
定理四:设m>1,所有不同的奇因数是,(g,m)=1,则g是模m的原根的充要条件是:
1<=i<=k
指标,n次剩余部分:
现在我们来研究同余式 (a,m)=1,有解的条件以及解数,注意现在的m=p^a或者2p^a,,g是模m的一个原根。
若(n,c)=d ,(a,m)=1,则上述同余式有解的充要条件是d|inda,并且在有解的条件下,解数为d。
在模m的一个简化剩余系中,n次剩余的个数是
定理一:若r通过模c的最小非负完全剩余系,则g^r通过模m的一个简化剩余系。
证明:g是模m的一个原根,则对模m两两不同余,又因为(g,m)=1,所以(g^r,m)=1
因此是模m的一个简化剩余系。
定理一:设a是一整数,(a,m)=1,若对模m的一个原根g,有一整数r存在使得下式
成立,则r就叫做以g为底的a对模m的一个指标,记为r=inda。
经典题目:HDU3731
扩展Baby Step Giant Step深入学习请戳这里
求解A^x = B (mod C) 中 0 <= x < C 的解。
本算法的安全性更高。
- #include<stdio.h>
- #include<string.h>
- #include<math.h>
- #include<stdlib.h>
- #define CC(m ,what) memset(m , what , sizeof(m))
- typedef long long LL ;
- LL A, B ,C ;
- const int NN=99991 ;
- bool hash[NN];
- int idx[NN];
- int val[NN];
- void insert(int id , LL vv)
- {
- LL v = vv % NN ;
- while( hash[v] && val[v]!=vv)
- {
- v++ ;
- if(v == NN)
- v-=NN ;
- }
- if(!hash[v])
- {
- hash[v] = 1;
- val[v] = vv ;
- idx[v] = id ;
- }
- }
- int find(LL vv)
- {
- LL v = vv % NN ;
- while( hash[v] && val[v]!=vv)
- {
- v++ ;
- if(v == NN) v-=NN;
- }
- if( !hash[v] ) return -1;
- return idx[v];
- }
- void ex_gcd(LL a , LL b , LL& x , LL& y)
- {
- if(b == 0)
- {
- x = 1 ;
- y = 0 ;
- return;
- }
- ex_gcd(b ,a%b,x,y);
- LL t = x;
- x = y;
- y=t-a/b*y ;
- }
- LL gcd(LL a,LL b){
- while( a%b != 0){
- LL c = a ;
- a = b ;
- b = c % b ;
- }
- return b ;
- }
- LL baby_step(LL A, LL B , LL C)
- {
- LL ans = 1 ;
- for(LL i=0;i<=50;i++)
- {
- if(ans == B) return i ;
- ans = ans * A % C ;
- }
- LL tmp , d = 0 ;
- LL D = 1 % C ;
- while( (tmp=gcd(A,C)) != 1 )
- {
- if(B % tmp) return -1 ;
- d++ ;
- B/=tmp ;
- C/=tmp ;
- D = D*A/tmp%C ;
- }
- CC( hash , 0) ;
- CC( idx, -1) ;
- CC(val , -1) ;
- LL M = ceil( sqrt(C*1.0) ) ;
- LL rr = 1 ;
- for(int i=0;i<M;i++)
- {
- insert(i, rr) ;
- rr = rr * A % C ;
- }
- LL x, y ;
- for(int i=0;i<M;i++)
- {
- ex_gcd(D, C , x, y) ;
- LL r = x * B % C;
- r = (r % C + C) % C ;
- int jj = find( r ) ;
- if(jj != -1)
- {
- return LL(i)*M + LL(jj) + d ;
- }
- D = D * rr % C ;
- }
- return -1 ;
- }
- int main()
- {
- while(~scanf("%I64d %I64d %I64d",&A,&C,&B))
- {
- if(A+B+C == 0 ) break ;
- LL res = baby_step(A,B,C) ;
- if( res == -1 )
- {
- printf("No Solution\n");
- continue ;
- }
- printf("%I64d\n",res);
- }
- return 0 ;
- }
下面的代码有时会超时。
- #include <iostream>
- #include <string.h>
- #include <math.h>
- #define LL long long
- #define MAXN 2000010
- struct HashNode
- {
- LL id;
- LL data;
- LL next;
- };
- HashNode hash[MAXN<<1];
- bool flag[MAXN<<1];
- LL top;
- void Insert(LL a,LL b)
- {
- LL k=b&MAXN;
- if(flag[k]==false)
- {
- flag[k]=true;
- hash[k].next=-1;
- hash[k].id=a;
- hash[k].data=b;
- return;
- }
- while(hash[k].next!=-1)
- {
- if(hash[k].data==b) return;
- k=hash[k].next;
- }
- if(hash[k].data==b) return;
- hash[k].next=++top;
- hash[top].next=-1;
- hash[top].id=a;
- hash[top].data=b;
- }
- LL Find(LL b)
- {
- LL k=b&MAXN;
- if(flag[k]==false) return -1;
- while(k!=-1)
- {
- if(hash[k].data==b) return hash[k].id;
- k=hash[k].next;
- }
- return -1;
- }
- LL gcd(LL a,LL b )
- {
- return b? gcd(b,a%b):a;
- }
- LL ext_gcd(LL a,LL b,LL &x,LL &y )
- {
- LL t,ret;
- if(b==0)
- {
- x=1;
- y=0;
- return a;
- }
- ret=ext_gcd(b,a%b,x,y);
- t=x;
- x=y;
- y=t-a/b*y;
- return ret;
- }
- LL mod_exp(LL a,LL b,LL m)
- {
- LL ret=1;
- a%=m;
- while(b)
- {
- if(b&1)
- {
- ret=ret*a%m;
- b--;
- }
- a=a*a%m;
- b>>=1;
- }
- return ret;
- }
- LL BabyStep_GiantStep(LL A,LL B,LL C)
- {
- top=MAXN;
- B%=C;
- LL tmp=1,i;
- for(i=0; i<=100;tmp=tmp*A%C,i++)
- if(tmp==B%C) return i;
- LL D=1,cnt=0;
- while((tmp=gcd(A,C))!=1)
- {
- if(B%tmp) return -1;
- C/=tmp;
- B/=tmp;
- D=D*A/tmp%C;
- cnt++;
- }
- LL M=(LL)ceil(sqrt(C+0.0));
- for(tmp=1,i=0;i<=M;tmp=tmp*A%C,i++)
- Insert(i,tmp);
- LL x,y,K=mod_exp(A,M,C);
- for(i=0;i<=M;i++)
- {
- ext_gcd(D,C,x,y); // D * X = 1 ( mod C )
- tmp=((B*x)%C+C)%C;
- if((y=Find(tmp))!=-1)
- return i*M+y+cnt;
- D=D*K%C;
- }
- return -1;
- }
- int main()
- {
- LL A,B,C;
- while(std::cin>>A>>C>>B)
- {
- if(A==0&&B==0&&C==0) break;
- memset(flag,0,sizeof(flag));
- LL tmp=BabyStep_GiantStep(A,B,C);
- if(tmp==-1)
- std::cout<<"No Solution"<<std::endl;
- else
- std::cout<<tmp<<std::endl;
- }
- return 0;
- }
注意对于HDU2815题解法一样,只是N>=P时无解。