【数论】求逆元的几种方式 【数论】求逆元的几种方式 >>>>方法介绍 >>>>扩展欧几里得求逆元的例题

逆元在数论中同余方程的很多地方都会用到,这里我们来整理一下求逆元的几种方式(*・ω< ) 

>>>>方法介绍

【方式一】费马小定理

费马小定理:ap-1Ξ1 (mod p)  前提:p是质数且gcd(a,p)=1

则有ap-2  *a Ξ 1 (mod p)

由逆元的意义可知:a与x相乘,若在mod p的意义下与1同余,那么x是a在模p意义下的逆元

所以ap-2  是a在模p意义下的逆元

用快速幂求ap-2即可

【方式二】扩展欧几里得

费马小定理解决的范围仅为  p是质数且gcd(a,p)=1

用扩展欧几里得做法可以算是通解

设y为我们已知的数,x为y的逆元,由逆元的定义可知:x*y Ξ 1 (mod p)

用扩展欧几里得展开,得到x*y+t*p =1

注意:求得的解x还要除以gcd(y,p),即除去同时扩大的倍数

下面有一道例题大家可以练练手

【方式三】递推

递推的方法相较前两个方法来说不常使用,在这里我们只给结论

我才不会说是因为我不会证明呢(逃

inv[i]=(-p/i*inv[p%i]%p+p)%p

inv[i]就是 i 的逆元

>>>>扩展欧几里得求逆元的例题

【题目】

Mr. Hu 又来让你帮忙解方程了。

方程是这样的:x1 + x1 + x3 + · · · + xn = m (xi ≥ 0 ∀1 ≤ i ≤ n)

Mr. Hu 希望你求出这个 n 元一次方程的整数解有多少个,因为解的个数有可能变得很大,所以 Mr. Hu只需要你输出解的个数取模于 mod。

【输入格式】
第 1 行,包含一个整数:T,表示询问个数
接下来 T 行,每行包含三个整数:n m mod
【输出格式】
输出 T 行,每行输出解的个数模对应 mod

【输入样例】

1

2 3 13

【输出样例】

4

【数据范围及约定】

样例中,解分别是:(3, 0),(2, 1),(1, 2),(0, 3)
• 对于 30% 的数据,1 ≤ n, m ≤ 6,mod = 108 + 7,T = 1
• 对于 70% 的数据,1 ≤ n, m ≤ 103,n + m ≤ mod ≤ 108 + 7,mod 是一个素数,1 ≤ T ≤ 100
• 对于余下 30% 的数据,1 ≤ n, m ≤ 103,n + m ≤ p, q ≤ 104,mod = p*q,p, q 是素数,1 ≤ T ≤ 103

>>>>分析

把每一个数的位置看做一个盒子,一共n个。把m看成m个球

问题等价于:“将m个球放进n个盒子,盒子可以为空”(解可以为0)的方案数

我们考虑使用隔板法,在这里介绍一下

题目:将5个完全相同的小球放入3个不同的盒子,盒子可以为空,一共有多少种放法?

解析:将5个小球放入3个盒子,需要2个隔板(分成三个区间,每个区间对应一个盒子)

因为允许有盒子为空,不符合隔板法的原理,那我们就再加上3个小球,保证每个盒子都至少分到一个小球(分完后再减去)

然后题目就变成了8个完全相同的小球放入三个不同的盒子,不可以为空。

8个球里一共有7个空位(两个球之间一个空位)需要在这7个空位里加入两个隔板来分隔成三个区间,那么一共有C(7,2)种不同的放法

那么这道题最终要求的就是C(n+m-1,n-1)

组合数的公式是C(m,n)=n!/((n-m)!*m!)将n!与m!约分,即为求(m+1)*(m+2)*....*n

原式转化为C(m,n)=(m+1)*(m+2)*....*n/(n-m)!

那么我们只需要用扩展欧几里得求出(n-m)!就可以了

代码如下

>>>>代码

#include<bits/stdc++.h>
#define ll long long
#define L I64
using namespace std;
int T;
ll p;
ll exgcd(ll a,ll &x,ll b,ll &y)
{
    if(b==0)
    {
        x=1,y=0;
        return a; 
    }
    ll xx,yy,d=exgcd(b,xx,a%b,yy);
    x=yy;
    y=xx-a/b*yy;
    return d;
}
ll C(ll m,ll n)
{
    ll jcn=1,jcm=1;
    for(int i=m+1;i<=n;i++)  jcm=jcm*i%p;
    for(ll i=1;i<=n-m;i++)   jcn=jcn*i%p;
    ll x,y,gcd=exgcd(jcn,x,p,y);
    x=x/gcd;
    return (jcm*x%p+p)%p;
}
ll n,m;
int main()
{
//    freopen("eqution.in","r",stdin);
//    freopen("eqution.out","w",stdout);
    scanf("%d",&T);
    while(T--)
    {
        scanf("%Ld%Ld%Ld",&n,&m,&p);
        printf("%Ld
",C(n-1,n+m-1));
    }
    return 0;
}
戳这里鸭

完结撒花✿✿ヽ(°▽°)ノ✿

感谢 被称为std的sxk大佬的思路!