HDU 2815 Mod Tree【扩展Baby Step Giant Step解决离散对数问题】
转载请注明出处,谢谢 http://blog.csdn.net/ACM_cxlove?viewmode=contents by---cxlove
又是AC神,数论帝,太强了。
对于A^X==B(MOD C)的情况,如果A与C不互质,那么普通的baby_step giant_step 便不能解决,AC给出了消因子的做法,并且
有证明
推论 2:
令 AA * A^b = B(mod C)
那么解存在的必要条件为: 可以得到至少一个可行解 A^b = X (mod C)
使上式成立
推论3
AA * A^b = B(mod C)
中解的个数为 (AA,C)
由推论3 不难想到对原始Baby Step Giant Step的改进
For I = 0 -> m
For any solution that AA * X = B (mod C)
If find X
Return I * m + j
而根据推论3,以上算法的复杂度实际在 (AA,C)很大的时候会退化到几乎O(C)
归结原因,是因为(AA,C)过大,而就是(A,C)过大
于是我们需要找到一中做法,可以将(A,C)减少,并不影响解
下面介绍一种“消因子”的做法
一开始D = 1 mod C
进行若干论的消因子,对于每次消因子
令 G = (A,C[i]) // C[i]表示经过i轮消因子以后的C的值
如果不存在 G | B[i] //B[i]表示经过i轮消因子以后的B的值
直接返回无解
否则
B[i+1] = B[i] / G
C[i+1] = C[i] / G
D = D * A / G
具体实现只需要用若干变量,细节参考代码
假设我们消了a'轮(假设最后得到的B,C分别为B',C')
那么有
D * A^b = B' (mod C')
于是可以得到算法
for i = 0 -> m
解 ( D* (A^m) ^i ) * X = B'(mod C')
由于 ( D* (A^m) ^i , C') = 1 (想想为什么?)
于是我们可以得到唯一解
之后的做法就是对于这个唯一解在Hash中查找
这样我们可以得到b的值,那么最小解就是a' + b !!
现在问题大约已近解决了,可是细心看来,其实还是有BUG的,那就是
对于
A^x = B(mod C)
如果x的最小解< a',那么会出错
而考虑到每次消因子最小消 2
故a'最大值为log(C)
于是我们可以暴力枚举0->log(C)的解,若得到了一个解,直接返回
否则必然有 解x > log(C)
PS.以上算法基于Hash 表,如果使用map等平衡树维护,那么复杂度会更大
#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>#include<cmath>#define LL __int64#define N 1000000using namespace std;struct Node{int idx;int val;}baby[N];bool cmp(Node n1,Node n2){return n1.val!=n2.val?n1.val<n2.val:n1.idx<n2.idx;}int gcd(int a,int b){return b==0?a:gcd(b,a%b);}int extend_gcd(int a,int b,int &x,int &y){if(b==0){x=1;y=0;return a;}int gcd=extend_gcd(b,a%b,x,y);int t=x;x=y;y=t-a/b*y;return gcd;}int inval(int a,int b,int n){int e,x,y;extend_gcd(a,n,x,y);e=((LL)x*b)%n;return e<0?e+n:e;}int PowMod(int a,int b,int MOD){LL ret=1%MOD,t=a%MOD;while(b){if(b&1)ret=((LL)ret*t)%MOD;t=((LL)t*t)%MOD;b>>=1;}return (int)ret;}int BinSearch(int num,int m){int low=0,high=m-1,mid;while(low<=high){mid=(low+high)>>1;if(baby[mid].val==num)return baby[mid].idx;if(baby[mid].val<num)low=mid+1;elsehigh=mid-1;}return -1;}int BabyStep(int A,int B,int C){LL tmp,D=1%C;int temp;for(int i=0,tmp=1%C;i<100;i++,tmp=((LL)tmp*A)%C)if(tmp==B)return i;int d=0;while((temp=gcd(A,C))!=1){if(B%temp) return -1;d++;C/=temp;B/=temp;D=((A/temp)*D)%C;}int m=(int)ceil(sqrt((double)C));for(int i=0,tmp=1%C;i<=m;i++,tmp=((LL)tmp*A)%C){baby[i].idx=i;baby[i].val=tmp;}sort(baby,baby+m+1,cmp);int cnt=1;for(int i=1;i<=m;i++)if(baby[i].val!=baby[cnt-1].val)baby[cnt++]=baby[i];int am=PowMod(A,m,C);for(int i=0;i<=m;i++,D=((LL)(D*am))%C){int tmp=inval(D,B,C);if(tmp>=0){int pos=BinSearch(tmp,cnt);if(pos!=-1)return i*m+pos+d;}}return -1;}int main(){int A,B,C;while(scanf("%d%d%d",&A,&C,&B)!=EOF){if(B>=C){puts("Orz,I can’t find D!");continue;}int ans=BabyStep(A,B,C);if(ans==-1)puts("Orz,I can’t find D!");elseprintf("%d\n",ans);}return 0;}