• 【数论】求逆元的几种方式


    【数论】求逆元的几种方式

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

    >>>>方法介绍

    【方式一】费马小定理

    费马小定理: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大佬的思路!

  • 相关阅读:
    filp_open/filp_close/vfs_read/vfs_write
    memcpy一种实现方法
    memset函数的实现&printf函数几种输出格式的输出结果
    break退出循环分析
    定义指针变量作为返回值函数执行时报 段错误(核心已转储)
    node实现防盗链
    js实现输入密码之延迟星号和点击按钮显示或隐藏
    rem适配
    使用字蛛教程以及遇到的bug
    es6学习笔记-proxy对象
  • 原文地址:https://www.cnblogs.com/psyyyyyy/p/10505449.html
Copyright © 2020-2023  润新知