逆元的求法

35 篇文章 0 订阅

题目传送门

题目大意:给你一个正整数n,一个模数p,要你求从1~n中每一个数的逆元。

数据范围:1≤n≤3×106 ,n<p<20000528;保证p是质数。

方法1:费马小定理
对于p是质数的情况下,我们可以用快速幂进行求逆元,原理是费马小定理。
费马小定理:对于质数p,ap-1 ≡ 1(mod p),我们要求a的在模p意义下的逆元的时候,有a*inv(a)≡1(mod p),又根据根据费马小定理, a * ap-2≡1(mod p),所以我们有inv(a)≡ap-2,所以我们可以用快速幂很快的求出a在模p意义下的逆元。

在你用方法1写完链接的这道题之后,会发现TLE了,因为这个时间复杂度为nlogp,在500ms的要求下是跑不完的,所以我们要另辟蹊径。

方法2:线性算法(用于求一连串数字的逆元)
首先,1的逆元是1。
假设我们有一个数字p,令p=k * i+r,即k是 x/i 的商,r是余数。
则 :k * i+r≡0(mod p),
两边同时乘:inv( i ) * inv( r )
则得到:k * inv ( r )+inv(i)≡0
即:inv(i)=-k * inv( r )
即:inv(i)=-(p/i) * inv(p%i).
继续转化:inv(i)=(p-p/i) * inv(p%i)
而p%i肯定要比i小,之前求过,所以可以直接拿来用。
写成代码就是

inv[1] = 1;
for(int i = 2; i < p; ++ i)
    inv[i] =(ll) (p - p / i) * inv[p % i] % p;

记得开long long,因为有时候逆元比较大,乘法容易爆精度。

方法3:拓展欧几里得(exgcd)
exgcd是求不定方程ax+by=c的解,由逆元定义,a * inv(a)≡1(mod p),即a * inv(a)+p * y=1,由于a,p已知,这个式子就可以用exgcd求解。

代码

void exgcd(int a,int b,int &x,int &y)
{
    if(!b)
    {x=1;y=0;return ;}
    exgcd(b,a%b,x,y);
    int t=y;
    y=x-a/b*y;
    x=t;
}
signed main()
{
    int n,p;
    cin>>n>>p;
    int x,y;
    for(int i=1;i<=n;i++)
    {
        exgcd(i,p,x,y);
        if(x>0&&x%p!=0)
            x=x%p;
        else
            x=x%p+p;
        cout<<x<<endl;
    }
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值