【训练题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;
}

猜你喜欢

转载自blog.csdn.net/weixin_45775438/article/details/111733358