【训练题22:线性求逆元】【模板】乘法逆元 | 洛谷 P3811

【模板】乘法逆元

难度

普 及 / 提 高 \color{yellow}普及/提高 /
板子题,给出两种方法

题意

n , p n,p n,p
求出所有 i ∈ [ 1 , n ] i\in[1,n] i[1,n]在模 p p p 情况下的逆元 i − 1 i^{-1} i1

一个一个求肯定是 O ( N log ⁡ N ) O(N\log N) O(NlogN),容易超。这里给出的两种都为 O ( N ) O(N) O(N)的。

数据范围

n ≤ 3 × 1 0 6 n\le 3\times10^6 n3×106
n < p < 20000528 n<p<20000528 n<p<20000528 p p p 是质数

方法1:阶乘逆元法

  • 我们可以 O ( N ) O(N) O(N) 预处理出所有 ( i ! ) − 1 (i!)^{-1} (i!)1,方法如下:
    • 首先算出 n ! n! n! ,可以直接递推获得。
    • 再算出 ( n ! ) − 1 (n!)^{-1} (n!)1,使用费马小定理即可,即 ( n ! ) − 1 ≡ ( n ! ) p − 2 ( m o d p ) (n!)^{-1}\equiv(n!)^{p-2}\pmod{p} (n!)1(n!)p2(modp)
    • 接下来,递推 ( i ! ) − 1 = ( ( i + 1 ) × ( i + 1 ) ! − 1 ) % p (i!)^{-1}=((i+1)\times(i+1)!^{-1})\% p (i!)1=((i+1)×(i+1)!1)%p 即可。
  • 计算 ( i ) − 1 = ( i ! ) − 1 × ( i − 1 ) ! % p (i)^{-1}=(i!)^{-1}\times (i-1)!\%p (i)1=(i!)1×(i1)!%p 即可。

方法2:线性求逆元

  • 我们求模 p p p i i i 的逆元,我们使用带余除法写出表达式子:
  • p = i × k + r p=i\times k+r p=i×k+r,其中 0 < r < i 0<r<i 0<r<i
  • 其中 k = p i k=\frac{p}{i} k=ip为商, r = p % i r=p\%i r=p%i为余数。
  • 接下来我们写出同余等式的表达式子:
  • i × k + r ≡ 0 ( m o d p ) i\times k+r\equiv 0\pmod{p} i×k+r0(modp)
  • 两边同时除以 r × i r\times i r×i,是为了得到 i − 1 i^{-1} i1这一项在单独的一边。我们得到:
  • r − 1 × k + i − 1 ≡ 0 ( m o d p ) r^{-1}\times k+i^{-1}\equiv 0\pmod{p} r1×k+i10(modp) 移项可得:
  • i − 1 ≡ −   r − 1 × k ( m o d p ) i^{-1}\equiv -\ r^{-1}\times k\pmod{p} i1 r1×k(modp) 我们把 r r r k k k 带入可得:
  • i − 1 ≡ −   ( p % i ) − 1 × p i ( m o d p ) i^{-1}\equiv -\ (p\%i)^{-1}\times \frac{p}{i}\pmod{p} i1 (p%i)1×ip(modp)
    接下来写成代码即可。
    注意一下,由于里面有负号,取模意义下不会出现负号,负数取模完加上模数再取模即可。
/*
 _            __   __          _          _
| |           \ \ / /         | |        (_)
| |__  _   _   \ V /__ _ _ __ | |     ___ _
| '_ \| | | |   \ // _` | '_ \| |    / _ \ |
| |_) | |_| |   | | (_| | | | | |___|  __/ |
|_.__/ \__, |   \_/\__,_|_| |_\_____/\___|_|
        __/ |
       |___/
*/
const int MAX = 3e6+50;
const ll  MOD = 19260817;
ll Inv[MAX];
int main()
{
    int n,p;
    scanf("%d%d",&n,&p);
    Inv[1] = 1;
    puts("1");
    for(int i = 2;i <= n;++i){
        Inv[i]=(p-(p/i)*Inv[p%i]%p)%p;
        printf("%lld\n",Inv[i]);
    }
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值