O(n)递(逆)推求乘法逆元

Posted acwing_zyy

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了O(n)递(逆)推求乘法逆元相关的知识,希望对你有一定的参考价值。

题目链接

P3811 【模板】乘法逆元


题目描述

给定 \\(n,p\\), 求 \\(1\\sim n\\) 中所有整数在模 \\(p\\) 意义下的乘法逆元。

输入格式

一行两个正整数 \\(n,p\\)

输出格式

输出 \\(n\\) 行,第 \\(i\\) 行表示 \\(i\\) 在模 \\(p\\) 下的乘法逆元。

输入

10 13

输出

1
7
9
10
8
11
2
5
3
4

说明/提示

\\(1 \\leq n \\leq 3 \\times 10 ^ 6, n < p < 20000528\\)
输入保证 \\(p\\) 为质数。

解题思路

线性求逆元

  1. \\(1^{−1}≡1(mod\\,p)\\)
  2. \\(p=k\\times i +r,(1<r<i<p)\\), \\(k\\)\\(p / i\\) 的商,\\(r\\) 是余数,则:

\\[k\\times i+r≡0(mod\\,p) \\]

然后乘上\\(i^{-1} ,r^{-1}\\) 就可以得到:

\\[k*r^{-1}+i^{-1}\\equiv 0 \\pmod p \\]

\\[i^{-1}\\equiv -k*r^{-1} \\pmod p \\]

\\[i^{-1}\\equiv -\\lfloor \\frac{p}{i} \\rfloor*(p \\bmod i)^{-1} \\pmod p \\]

可以发现,这是个递推式~

  • 时间复杂度:\\(O(n)\\)

代码

#include<bits/stdc++.h>
using namespace std;
const int N=3e6+10;
int inv[N];
int n,p;
int main()
{
    scanf("%d%d",&n,&p);
    inv[1]=1;
    puts("1");
    for(int i=2;i<=n;i++)
    {
        inv[i]=1ll*(-p/i+p)*inv[p%i]%p;
        printf("%d\\n",inv[i]);
    }
    return 0;
}

逆推求阶乘逆元

假设我们已经求出 \\(n!\\) 的逆元 \\((n!)^{-1}\\),现在我们要求 \\((n-1)!\\) 的逆元,而:

\\[n! \\times (n!)^{-1} \\equiv1 (\\bmod p) \\Longleftrightarrow (n-1)! \\times n (n!)^{-1} \\equiv1 (\\bmod p) \\]

\\((n-1)!\\) 的逆元为 \\(n (n!)^{-1}\\),反过来,\\(n\\) 的逆元为 \\((n-1)! \\times (n!)^{-1}\\)

  • 时间复杂度:\\(O(n)\\)

代码

#include<bits/stdc++.h>
using namespace std;
const int N=3e6+10;
int n,p;
int fact[N],inv_fact[N];
int ksm(int a,int b)
{
    int res=1%p;
    for(;b;b>>=1)
    {
        if(b&1)res=1ll*res*a%p;
        a=1ll*a*a%p;
    }
    return res;
}
int main()
{
    scanf("%d%d",&n,&p);
    fact[0]=1;
    for(int i=1;i<=n;i++)fact[i]=1ll*fact[i-1]*i%p;
    inv_fact[n]=ksm(fact[n],p-2);
    for(int i=n-1;i;i--)
        inv_fact[i]=1ll*(i+1)*inv_fact[i+1]%p;
    for(int i=1;i<=n;i++)
        printf("%d\\n",int(1ll*fact[i-1]*inv_fact[i]%p));
    return 0;
}

以上是关于O(n)递(逆)推求乘法逆元的主要内容,如果未能解决你的问题,请参考以下文章

乘法逆元的线性筛法

模板求1~n的整数的乘法逆元

乘法逆元入门

P5431 模板乘法逆元2

P3811 模板乘法逆元

求乘法逆元的几种姿势