扩展欧几里得是用于求解不定方程、线性同余方程和乘法逆元的常用算法。
下面是代码:
1 function Euclid(a,b:int64;var x,y:int64):int64; 2 var t:int64; 3 begin 4 if b=0 then 5 begin 6 x:=1;y:=0;exit(a); 7 end else 8 begin 9 Euclid:=Euclid(b,a mod b,x,y); 10 t:=x;x:=y;y:=t-(a div b)*y; 11 end; 12 end;
下面出现了其中后两个应用。(虽然个人认为不定方程和同余方程可以互相转换)
POJ1061
线性同余简单应用。
1 program poj1061; 2 var x0,y0,m,n,L,a,b,d,x,y,tem,c:int64; 3 4 function f(a:int64):int64; 5 begin 6 if a<0 then a:=a+(-a) div L*L; 7 while a<=0 do a:=a+L; 8 exit(a); 9 end; 10 11 function Euclid(a,b:int64;var x,y:int64):int64; 12 var t:int64; 13 begin 14 if b=0 then 15 begin 16 x:=1;y:=0;exit(a); 17 end else 18 begin 19 Euclid:=Euclid(b,a mod b,x,y); 20 t:=x;x:=y;y:=t-(a div b)*y; 21 end; 22 end; 23 24 begin 25 readln(x0,y0,m,n,L); 26 a:=f(m-n);b:=L; 27 d:=Euclid(a,b,x,y); 28 c:=f(y0-x0); 29 // 由于扩展欧几里得算法里要求最大公约数,所以要处理成正数 30 // 根据同余方程的性质,a,b,c加上L对答案不会有影响。 31 if c mod d<>0 then 32 begin 33 writeln('Impossible'); 34 // 同余方程有整数解的条件。 35 halt; 36 end; 37 x:=x*c div d; 38 if x<0 then x:=x+(-x) div (b div d)*(b div d); 39 while x<0 do x:=x+(b div d); 40 //这里是保证跳的步数为自然数 41 if x-(b div d)>=0 then x:=x-x div (b div d)*(b div d); 42 while x-(b div d)>=0 do x:=x-(b div d); 43 //这里是保证跳的步数是最小步数 44 writeln(x); 45 end.
POJ1845
由于姿势不大对...交了很多遍处理了很多细节才过。
因此觉得这道题挺好的...因为本来觉得只是一道求逆元的模板题,但实际上想要A掉它并不容易。
首先先讲一下算法部分。
求a^b的约数和。
= (p1^0+p1^1+...p1^k1)*(p2^0+p2^1...+p2^k2)...*(pn^0+pn^1+...pn*kn) (p为a的质因子,ki为第i个质因子的个数)
我们可以用素数拆分,然后对于每一部分,用等比数列求和公式。
分子部分可以用取模的快速幂解决。
分母部分乘上其逆元。
大体的思路已经构建好了。
但是细节超级超级多.....>_< 已经被搞疯啦...直接在代码里贴出来好了。
1 program poj1845; 2 const tt=9901; 3 var a,b,ans,k:int64; 4 i:longint; 5 6 function Euclid(a,b:int64;var x,y:int64):int64; 7 var t:int64; 8 begin 9 if b=0 then 10 begin 11 x:=1;y:=0;exit(a); 12 end else 13 begin 14 Euclid:=Euclid(b,a mod b,x,y); 15 t:=x;x:=y;y:=t-(a div b)*y; 16 end; 17 end; 18 19 function mul(a,b:int64):int64; 20 var ans,w:int64; 21 begin 22 ans:=1;w:=a mod tt; 23 while b>0 do 24 begin 25 if b and 1=1 then ans:=(ans*w)mod tt; 26 w:=(w*w) mod tt; 27 b:=b >> 1; 28 end; 29 exit(ans); 30 end; 31 32 function inverse(p:int64):int64; 33 var x,y:int64; 34 begin 35 if Euclid(p,tt,x,y)<>1 then exit(-1) else 36 begin 37 while x<tt do inc(x,tt); 38 x:=x mod tt; 39 // 我们需要找到一个正的逆元,根据x= x0+b/d*t可以得到,d=1,b=tt。所以只要不停加上tt即可。 40 // 虽然刚开始的直觉就是不停加上tt直到找到一个正的为止...但是实际上是有理论依据的。 41 exit(x); 42 end; 43 end; 44 45 begin 46 while not eof do 47 // 由于被多组数据坑过...所以不管怎样还是加上为好 48 begin 49 readln(a,b); 50 if a=0 then 51 begin 52 writeln(0); 53 // 这是一个细节需要特判 54 continue; 55 end; 56 ans:=1; 57 for i:=2 to trunc(sqrt(a)) do if a mod i=0 then 58 begin 59 k:=0; 60 while a mod i=0 do 61 begin 62 inc(k);a:=a div i; 63 end; 64 k:=k*b; 65 ans:=((ans*((mul(i,k+1)+tt-1) mod tt)) mod tt*inverse(i-1))mod tt; 66 // 本来这里的inverse(i-1)也需要处理i mod tt=1的情况,但是由于数据范围i的上限正好是7000多不用考虑。 67 end; 68 // 刚开始下面的一块都忘了QAQ 69 if a<>1 then 70 begin 71 if a mod tt=1 then 72 begin 73 ans:=ans*(b+1) mod tt; 74 // 这是最容易忽略的一个细节。当a mod tt=1的时候,inverse(i)是算不出来的,这个时候思考一下便发现,a mod tt=1 75 // a^k mod tt=1,所以最后mod tt余数就是b+1. 76 // 刚开始由于直接输出了(b+1) WA了一次...后来发现剩下的a不一定是a本身。 77 end else 78 begin 79 i:=a;k:=b; 80 ans:=((ans*((mul(i,k+1)+tt-1) mod tt)) mod tt*inverse(i-1)) mod tt; 81 end; 82 end; 83 writeln(ans); 84 end; 85 end. 86