EXgcd&&逆元(2019.3.2训练)【更新完成】

版权声明:欢迎转载,转载请标明作者和出处。 https://blog.csdn.net/ljw_study_in_CSDN/article/details/88084387

本次训练共5题,本文附AC代码和题目链接。

做题之前,先给出线性求a对p的逆元inv(a,p)的两个模板,时间复杂度均为O(n)。

模板1:递归求逆元

long long inv(long long a,long long p)
{return a==1?1:(p-p/a)*inv(p%a,p)%p;}

使用要求:
1.a、p互质
2.p为质数(比如9973,19260817)
3.a<p(要保证a<p,函数调用时写成inv(a%p,p)即可)
这三个条件必须同时满足才能保证逆元一定存在,否则有可能无限递归Runtime Error
比如inv(5,12),满足a、p互质且a<p,但是不满足p为质数,所以没有逆元inv(5,12),不能用这个模板

模板2:递推求逆元(逆元不存在也可使用,若逆元inv[i]不存在则inv[i]=0)

long long n,p,i,inv[1000010];
void get_inv()
{
    inv[1]=1;
    for(i=2;i<=n;i++)//n为能递推到的最大的a
    inv[i]=(p-p/i)*inv[p%i]%p;
}

使用要求:能递推到的最大的a不要超过1e6,否则请使用模板1或者用exgcd。
开数组要过本地编译器最大能开1e8,但是oj过不了(Runtime Error: Segmentation fault)
开1e7会超过内存64512k(Memory Limit Exceeded)
开1e6,数据小的话一般能过

A题 nefu 1746 A/B

#include <bits/stdc++.h>
using namespace std;
long long t,n,b,p=9973;
long long inv(long long a,long long p)
{return a==1?1:(p-p/a)*inv(p%a,p)%p;}
int main()
{
    ios::sync_with_stdio(false);
    cin>>t;
    while(t--)
    {
        cin>>n>>b;
        printf("%lld\n",(n*(inv(b%p,p)%p)%p));
    }
    return 0;
}

B题 nefu 1747 Romantic(hdu 2669)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll a,b,x,y,t,ans;
ll exgcd(ll a,ll b,ll &x,ll &y)
{
    if(b==0){x=1;y=0;return a;}
    ans=exgcd(b,a%b,x,y);
    t=x;
    x=y;
    y=t-a/b*y;
    return ans;
}
/*void exgcd(int a,int b,int &x,int &y)
{
    if(b==0){x=1;y=0;return;}//递归出口
    int x1,y1;
    exgcd(b,a%b,x1,y1);
    x=y1;
    y=x1-a/b*y1;
}*/
int main()
{
    ios::sync_with_stdio(false);
    while(cin>>a>>b)
    {
        ll d=exgcd(a,b,x,y);
        if(1%d!=0)printf("sorry\n");//无法整除
        else
        {
            while(x<0)x=x+b,y=y-a;
            printf("%lld %lld\n",x,y);
        }
    }
    return 0;
}

C题 nefu 1748 同余方程

#include <bits/stdc++.h>
using namespace std;
long long a,b;
long long inv(long long a,long long p)
{return a==1?1:(p-p/a)*inv(p%a,p)%p;}
int main()
{
    ios::sync_with_stdio(false);
    cin>>a>>b;
    printf("%lld\n",inv(a%b,b));
    return 0;
}

D题 nefu 1749 有理数取余

#include <bits/stdc++.h>
using namespace std;
long long a,b,p=19260817;
long long inv(long long a,long long p)
{return a==1?1:(p-p/a)*inv(p%a,p)%p;}
long long gcd(long long a,long long b)
{return b?gcd(b,a%b):a;}
int main()
{
    ios::sync_with_stdio(false);
    cin>>a>>b;
    if(gcd(b,p)==1)printf("%lld\n",(a%p)*(inv(b%p,p)%p)%p);
    else printf("Angry\n");
    return 0;
}

E题 nefu 1750 乘法逆元

#include <bits/stdc++.h>
using namespace std;
long long n,p,i,inv[3000010];
void get_inv()
{
    inv[1]=1;
    for(i=2;i<=n;i++)
    inv[i]=(p-p/i)*inv[p%i]%p;
}
int main()
{
    ios::sync_with_stdio(false);
    cin>>n>>p;
    get_inv();
    for(i=1;i<=n;i++)
    printf("%lld\n",inv[i]);
    return 0;
}

总结
扩展欧几里得、费马小定理+快速幂、线性求逆元是逆元的三种求法
1.扩展欧几里得:要求a、p互质,时间复杂度O(logap)
2.费马小定理+快速幂:要求p必须是质数,时间复杂度O(log2p)
3.线性求逆元:要求p必须是质数,时间复杂度O( P )
如果求一个数的逆元,扩展欧几里得是最佳的选择,
如果需要N个数的逆元,线性递推式最好的方式,
费马小定理+快速幂似乎没什么用武之地,
后两种都是要求p是质数的,当p不是质数,但p和a互质时,可以选择扩展欧几里得

求逆元几种方法的总结还可以去看看这篇文章https://blog.csdn.net/fsahfgsadhsakndas/article/details/51027338

猜你喜欢

转载自blog.csdn.net/ljw_study_in_CSDN/article/details/88084387