P3811 【模板】乘法逆元 题解

“对呀对呀!……回字有四样写法,你知道么?”
								——鲁迅《孔乙己》

逆元有四种求法

1、利用费马小定理

2、利用扩展欧几里得定理

3、求1~n的所有数的逆元,时间复杂度为 \(O(n)\)
具体地,我们首先可以知道\(1^{-1}=1\)
于是,对于模数\(p\),我们可以设\(p=ki+t,t<i\),即\(p/i=k\)余数为\(t\)
因此,我们可以知道\(ki+t \equiv 0 (mod\ p)\)
等式两边乘以\(i^{-1}t^{-1}\),得\(kt^{-1}+i^{-1} \equiv 0 (mod\ p)\)
移项,得\(i^{-1}t^{-1}\),得\(i^{-1} \equiv -kt^{-1} (mod\ p)\)
在柿子中,\(-k\)\(t^{-1}\) 都是已知的。
由此,我们得到了\(i\)的逆元的求法。(这么巧妙的算法到底谁发明的)
及时复习,不然啥都会忘掉的。。。。。。

4、求1~n的所有数的阶乘的逆元
这个东西在求大量的阻合数的时候会使用到吧
因为\((n+1)!=n!(n+1)\)
所以\([(n+1)!]^{-1}=(n+1)^{-1}(n!)^{-1}\)
好像之前做了一个和二项式定理有关的题目就用了这个然后我就裂开了。

5、求O(n)求n个数的逆元

计算出这些数的乘积,求出乘积的逆元,然后乘以前缀积和后缀积就是当前这个数的逆元。

image

第三种求法的代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define int long long
using namespace std;
inline int r()
{
	int s=0,k=1;char c=getchar();
	while(!isdigit(c))
	{
		if(c=='-')k=-1;
		c=getchar();
	}
	while(isdigit(c))
	{
		s=s*10+c-'0';
		c=getchar();
	}
	return s*k;
}
int n,p,a[10000001];
signed main()
{
	n=r();p=r();
	a[1]=1;
	for(int i=2;i<=n;i++)
	{
		int k=p/i,t=p%i;
		a[i]=((-k*a[t])%p+p)%p;
	}
	for(int i=1;i<=n;i++)printf("%lld\n",a[i]);
}
posted @ 2021-07-16 20:38  lei_yu  阅读(155)  评论(0编辑  收藏  举报