1467: Pku3243 clever Y - #include <Seter> - using namespace Orz;
1467: Pku3243 clever Y
http://www.zybbs.org/JudgeOnline/problem.php?id=1467
RunID | User | Problem | Result | Memory | Time | Language | Code Length | Submit Time |
206331 | Seter | 1467 | Accepted | 1432 kb | 52 ms | C/Edit | 1599 B | 2012-02-17 20:55:23 |
终于弄明白扩展小步大步算法了囧,我的数学真是太差了。。
AC大神的代码很好懂,但是解释就……我反正看不懂。
题意:解方程A^x=B(mod C)。
首先假设C是素数。那么特判掉A是C的倍数的情况后,(A,C)=1。
由于C是素数,根据XX小定理,可以证明A^C=A(mod C),于是如果存在x,最大可能的值就是C-1。
弄一个M=sqrt(C),令x=iM+j(0<=j<M),式子转化为(A^M)^i*A^j=B(mod C)。
再令p=(A^M)^i mod C,q=A^j mod C,则pq=B(mod C)。另外,(A,C)=1可以推出(p,C)=1。
我们从0开始枚举i直到i*M超过C-1,则相当于知道了p,要求q。
此时根据XX定理,[0,C)中有且仅有一个q,可以用exgcd算,至于exgcd...自己查!
这样我们就知道了q,问题转化为求是否存在A^j=q(mod C)。咦?这个和原题一样啊,难道递归?
注意到j<M=sqrt(C),已经比较小了,暴力即可!
当然,这个暴力的意思是,将A^0...A^(M-1) mod C用hash等保存下来,然后每次去里面找。
伪代码大概像这样:
1 2 3 4 5 6 7 8 9 | IF A mod C = 0 特判 M = sqrt(C) Insert (A^0...A^(M-1) mod C) into HASHMAP FOR i = 0 to C/M do p = (A^M)^i mod C q = Inval(p,B,C) //求p对C的逆元然后乘上B,求逆元用exgcd IF q is in HASHMAP return i * M + FIRST(q) //FIRST表示对于q,最早插入时的指数 done return FAIL |
当C不是素数的时候能否直接套用呢?当然不可以……最直接的问题就是,不一定存在逆元!
考虑一个G'同时是ABC的因数,令B'=B/G',C'=C/G',则当x不等于0时,(A/G')*A^(x-1)=B'(mod C')。
这样多弄几次,C的因数就越来越少,直到(A,C)=1。
那么如何选取G'呢?AC大神告诉你:不断取G'=gcd(A,C'),直到G'=1。如果任意一个G'不是B'的因数则一定无解。
假设取了r次G',然后所有G'的积是G,则问题变为(A^r/G)*A^(x-r)=B'(mod C'),令D=A^r/G(一定是整数),则由于此时(A,C')=1,所以(D,C')=1。
那么上面算法中只要变成求(D*p)对于C'的逆元就可以了,返回的答案还要加上r。
还有一点小问题,就是这样得出的答案是大于等于r的。但是即使每次G'=2,r最大也只有log2(C),那么这些再暴力求解就可以了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | #include <stdio.h> #include <math.h> #include <string.h> #define LL long long #define maxn 40000 #define hashp 50021 typedef struct _Hash{ struct _Hash*next; int v,t}Hash,*PHash; Hash st[maxn],*h[hashp],*stp,*p; inline exgcd(x,y,a,b) int *a,*b,x,y;{ int aa=*b=0,bb=*a=1,ta,tb; if (!x||!y) return -1; while (y){ ta=aa;tb=bb; aa=*a-x/y*aa;bb=*b-x/y*bb; *a=ta;*b=tb; ta=x;x=y;y=ta%y; } return x; } inline getint(){ int re=0,ch= getchar (); while (ch< '0' ||ch> '9' ) if ((ch= getchar ())<=0) return -1; for (;ch>= '0' &&ch<= '9' ;ch= getchar ())re=re*10+ch-48; return re; } inline PHash find(x,t,c) int x,t;_Bool c;{ int i=(x%hashp+hashp)%hashp;PHash r=h[i]; for (;r;r=r->next) if (r->v==x) return r; if (c) return 0;stp->next=h[i];stp->v=x;stp->t=t; return h[i]=stp++; } inline gcd(x,y){ int t; while (y)t=x,x=y,y=t%y; return x;} inline solve(a,b,c){ int i=-1,tmp,r=0,M,x;LL buf=1,D=1,K=1; for (stp=st;++i!=50;buf=buf*a%c) if (buf==b) return i; while ((tmp=gcd(a,c))!=1){ if (b%tmp) return -1; r++;c/=tmp;b/=tmp;D=D*a/tmp%c; }M= sqrt (c); memset (h,0,hashp<<2); for (buf=1,i=-1;++i!=M;K=K*a%c,buf=buf*a%c)find(( int )buf,i,0); for (i=-1;(LL)++i*M<=c;D=D*K%c){ exgcd(( int )D,c,&tmp,&x); tmp=(LL)tmp*b%c; if (tmp<0)tmp+=c; if (p=find(tmp,0,1)) return i*M+p->t+r; } return -1; }main(){ int a,b,c; while (a=getint(),c=getint(),b=getint())b>=c||(a=solve(a,b,c))<0? puts ( "No Solution" ): printf ( "%d\n" ,a); return 0; } |
