• hdu 1576 A/B(exgcd、乘法逆元+整数快速幂)


    Problem Description

    要求(A/B)%9973,但由于A很大,我们只给出n(n=A%9973)(我们给定的A必能被B整除,且gcd(B,9973) = 1)。

    Input

    数据的第一行是一个T,表示有T组数据。每组数据有两个数n(0 <= n < 9973)和B(1 <= B <= 10^9)。

    Output

    对应每组数据输出(A/B)%9973。

    Sample Input

    2
    1000 53
    87 123456789

    Sample Output

    7922
    6060

    解题思路:这道题有三种做法,①是扩展欧几里德算法,②是试探算法,③是乘法逆元。

    解法一:可以用解整数的不定方程来解决,即使用扩展欧几里德算法。

    根据题意,输入的n=A%9973(没有输入A),A%B=0(A必能被B整除),B与9973互质(gcd(B,9973)=1)。
    设x=(A/B)%9973(x是最终想计算的值),则9973k+x=A/B(k为整数:(A/B)/9973),得A=9973Bk+xB。
    因为n=A%9973与A=9973Bk+xB,所以xB%9973=n,得xB=n+9973y。故:(x/n)B+(-y/n)9973=1=gcd(B,9973),该方程有解。要求x和y,先求X=x/n和Y=-y/n,即先解方程BX+9973Y=1。最后,x=X*n。需要注意的是,求得的x有可能是负值,需要调整为正数,即x=(x+a)%a。

    顺便推导一下扩展欧几里得算法的计算过程:假设ax+by=gcd(a,b),由欧几里得算法可得gcd(a,b)=gcd(b,a%b),即ax+by=bx1+(a%b)y1=bx1+(a-a/b*b)y1=ay1+b(x1-a/b*y1),由恒等定理可得x=y1,y=x1-a/b*y1,这样我们就得到了求解x,y的方法:系数x,y基于x1,y1。终止条件:当b=0时,a为最大公约数。这时只需保证a的系数即x=1即可,y取任何值都不会由影响,因为此时b=0,所以我们不妨假设y=0,exgcd先递归到最后再回溯回来计算每个状态下的x',y',最终就可求得系数x,y的值。

    AC代码:

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 int exGcd(int a,int b,int &x,int &y)//exgcd算法
     4 {
     5     if(b==0){x=1;y=0;return a;}//ecgcd的终止状态为a=gcd,b=0
     6     int r=exGcd(b,a%b,x,y);//下一个状态:b*x1+(a%b)*y1=gcd(b,a%b),相当于欧几里得算法,先递归到b=0,然后返回每个状态下的公约数
     7     int t=x;x=y;y=t-a/b*y;//x=y',y=x'-a/b*y'
     8     return r;//返回当前的公约数给上一个状态
     9 }
    10 int main()
    11 {
    12     int t,n,b,x,y,a=9973;//a为质数
    13     cin>>t;
    14     while(t--){
    15         cin>>n>>b;
    16         exGcd(b,a,x,y);//方程:bx+ay=gcd(b,a)=1
    17         //if(x<0)x+=a;
    18         x=(x+a)%a;// x有可能为负,需要调整为正数
    19         cout<<x*n%a<<endl;//A=m*B,x=m/n --> m=x*n,(A/B)%9973=(x*n*B/B)%9973=(x*n)%9973
    20     }
    21     return 0;
    22 }

    解法二:试探法。根据题意,输入的n=A%9973(没有输入A),A%B=0(A必能被B整除),B与9973互质(gcd(B,9973)=1)。
    设x=(A/B)%9973(x是最终想计算的值,满足0<=x<9973),则9973k+x=A/B(k为整数:(A/B)/9973),得A=9973Bk+xB。
    因为n=A%9973与A=9973Bk+xB,所以xB%9973=n,得xB=n+9973y,亦得xB-n=9973y。故:(xB-n)%9973=0
    需要注意的是,变量类型为long long。

    AC代码:

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 int main()
     4 {
     5     int t;
     6     long long n,b;//要用long long不然会溢出(n接近10的4次方,b接近10的9次方,取最大时已经超int)
     7     cin>>t;
     8     while(t--){
     9         cin>>n>>b;
    10         for(int x=0;x<9973;x++)
    11             if((x*b-n)%9973==0){cout<<x<<endl;break;}//试探算法
    12     }
    13     return 0;
    14 }

     解法三:乘法逆元。∵(A/B)%9973=A%9973*B-1%9973=n*B-1%9973,根据费马小定理a(p-1)≡1(mod p),即a*a(p-2)≡1(mod p),又由乘法逆元:ax≡1(mod p)可得x=a(p-2)%p=a-1%p(注意:p是质数,且p不能整除a(即gcd(a,p)=1),x为a关于模p的乘法逆元)∴最终可以推得n*B-1%p=n*B(p-2)%p(这里的p=9973),套一下整数快速幂模板即可。

    AC代码:

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 typedef long long LL;
     4 const int mod=9973;
     5 LL mod_power(LL a,LL b){//整数快速幂
     6     LL ans=1;
     7     while(b){
     8         if(b&1)ans=ans*a%mod;
     9         a=a*a%mod;
    10         b>>=1;
    11     }
    12     return ans;
    13 }
    14 int main(){
    15     int t;LL n,B;
    16     while(cin>>t){
    17         while(t--){
    18             cin>>n>>B;
    19             cout<<n*mod_power(B,mod-2)%mod<<endl;//n*B^(p-2)%p
    20         }
    21     }
    22     return 0;
    23 }
  • 相关阅读:
    Android应用性能优化
    打造高质量Android应用:Android开发必知的50个诀窍
    毕向东day23--java基础-网络总结
    《编写高质量代码:改善Java程序的151个建议》
    最新java数组的详解
    主线程中一定不能放耗时操作,必须要开子线程,比如下载文件,不然会不让你拿到输入流--报错显示android.os.NetworkOnMainThreadException
    《Head First设计模式(中文版)》
    码表由来:ascll码-Gbk2312-GBK-Unicode-UTF-8
    《Java程序性能优化:让你的Java程序更快、更稳定》
    LeetCode 147. 对链表进行插入排序
  • 原文地址:https://www.cnblogs.com/acgoto/p/8486630.html
Copyright © 2020-2023  润新知