乘法逆元出门跳坑
欧拉函数证明出门打的
欧拉函数代码实现出门小黄车
许多题会用到乘法逆元这个玄学的东西,求一次还好,但是如果求1~p-1在mod p意义下的逆元,单个求的log级别显然不可取,所以我门需要线性处理一下
先说几个单个求的思路
方法一:先预处理欧拉函数,然后用快速幂求a^(φ(p)-1)
代码(线性欧拉函数)
void getphi()
{
int vis[M],phi[M],pri[M],tot;
for(int i=2;i<=n;i++)
{
if(!vis[i])//先判断i是不是素数
{
pri[++tot]=i;
phi[i]=i-1;//当 i 是素数时小于i的所有大于零的数都和i互素
}
for(int k=1;k<=tot;k++)
{
if(i*pri[k]>M) break;
vis[i*pri[k]]=1;//按照筛素数,筛掉i的倍数
if(i%pri[k]==0)//如果有一个质数是i的因子,那么phi(i*pri[k])=phi(i)*pri[k]
{
phi[i*pri[k]]=phi[i]*pri[k];break;
}
else phi[i*pri[k]]=phi[i]*(phi[pri[k]]);
//利用了欧拉函数的积性,两个数互质,那么phi(i*k)=phi(i)*phi(pri[k])
}
}
}
//快速幂自己写吧我就不放了
方法二:扩展欧几里得,将同余式转化为a*x+p*y=1,求解x,y
代码
//By AcerMo
#include
#include
#include
#include
#include
using namespace std;
int x,y,n,m;
int exgcd(int a,int b,int &x,int &y)
{
if (b==0)
{
x=1;y=0;return a;
}
else
{
int d=exgcd(b,a%b,x,y);
int z=x;x=y;y=z-y*(a/b);
return d;
}
}
int main()
{
scanf("%d%d",&n,&m);
exgcd(n,m,x,y);
cout<<(x%m+m)%m;
return 0;
}
方法三:o(n)求逆元表,没错就是打表(通过递推打表)
假如要求i在mod p意义下的逆元,我们令a=p%i,b=(p/i),则p=a+b*i,可得a+b*i ≡ 0 (mod p),则-a ≡ b*i (mod p),则i^(-1)≡-(b/a) (mod p),所以可得i的乘法逆元=-(p/i)*(p%i)^(-1),则可以递推得到所有逆元,因为p%i必定小于i,所以在处理i之前必定已经处理好了p%i
代码
void mutl(int mod)
{
int inv[M];inv[1]=1;//边界条件
for (int i=2;i<=M;i++)
inv[i]=((-(mod/i)*inv[mod%i])%mod+mod)%mod; //防止出现负数
return ;
}
从组合数那里飞过来的
思路
我们先处理出一个s=1 * 2 * ... * ns=1∗2∗...∗n,然后用费马小定理求一下它的逆元就是s=s^{p-2}s=sp−2,然后现在的s就相当于\frac{1}{1 * 2 * ... * n}1∗2∗...∗n1,所以我们发现1~n中任意一项的逆元都可以由现在的s在O(1)得到,如何得到?假如我们要求5的逆元,就相当于s * (1 * 2 * 3 * 4) * (6*...* n)s∗(1∗2∗3∗4)∗(6∗...∗n)也就是把分母里除5以外的所有数消去,就能得到5的逆元了,如何实现呢?我们预处理一个前缀积数组,然后倒序处理,他前面的数的积直接查表,后面数的积交给一个tmp,初始是1,每处理完一个数,就乘上这个数就好了
代码
//By AcerMo
#include
#include
#include
#include
#include
using namespace std;
const int M=3005000;
int n,m,s[M];
inline int fpow(int x,int y)
{
int z=1;
for (;y;x=(1LL*x*x)%m,y>>=1)
if (y&1) z=(1LL*z*x)%m;
return z;
}
signed main()
{
cin>>n>>m;s[0]=1;
for (int i=1;i<=n;i++)
s[i]=(1LL*s[i-1]*i)%m;
int s1=1,ni=fpow(s[n],m-2);
for (int i=n;i;i--)
{
s[i]=((1LL*ni*s[i-1])%m*1LL*s1)%m;
s1=(1LL*i*s1)%m;
}
for (int i=1;i<=n;i++) printf("%d\n",s[i]);
return 0;
}
如有错误请联系博主