线性求乘法逆元

乘法逆元出门跳坑

欧拉函数证明出门打的

欧拉函数代码实现出门小黄车

许多题会用到乘法逆元这个玄学的东西,求一次还好,但是如果求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;
}

如有错误请联系博主

 

 

 

 

你可能感兴趣的:(数论&&组合数学)