zoukankan      html  css  js  c++  java
  • [POJ1845&POJ1061]扩展欧几里得应用两例

    扩展欧几里得是用于求解不定方程、线性同余方程和乘法逆元的常用算法。

    下面是代码:

     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         
  • 相关阅读:
    第八章
    第十章
    第九章
    第七章
    第六章
    第五章
    第四章心得
    第二章心得
    第三章心得
    第一章心得
  • 原文地址:https://www.cnblogs.com/mjy0724/p/4371752.html
Copyright © 2011-2022 走看看