zoukankan      html  css  js  c++  java
  • 逆元

    对于(a/b)%MOD这个式子,是不可以写成(a%MOD/b%MOD)%MOD,但是我们可以通过求b的逆元,转化成乘法运算

    方程ax≡1(mod  p),的解称为a关于模p的逆,当gcd(a,p)==1(即a,p互质)时,方程有唯一解,否则无解。


    有如下几种求逆元的方式

    1.递推求逆元 逆元有这样一个公式:inv[i]=-p/i*inv[p%i]%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

    简单分析一下,大概就可以看成是有m个小球放入n个盒子里,通过夹棍法我们知道答案是C(m+n+1-1,,n-1)

    那我们就可以先处理出阶乘,再用O(1)的时间复杂度解决lalala

    #include<bits/stdc++.h>
    #define maxn 100005
    #define ll long long
    using namespace std;
    ll fac[maxn],invfac[maxn];
    ll C(int n,int m,int p)
    {
        return fac[n]*invfac[n-m]%p*invfac[m]%p;
    }
    int main()
    {
        int T;
        scanf("%d",&T);
        while(T--)
        {
            int n,m,p;
            scanf("%d%d%d",&n,&m,&p);
            fac[0]=invfac[0]=fac[1]=invfac[1]=1;
            for(int i=2;i<=m+n;i++)
              invfac[i]=-p/i*invfac[p%i]%p;
            for(int i=2;i<=m+n;i++) 
            {
                fac[i]=(fac[i-1]*i%p+p)%p;//阶乘
                invfac[i]=(invfac[i-1]*invfac[i]%p+p)%p;
            }
            printf("%I64d
    ",C(m+n-1,n-1,p));
        }
    }
    逆元阶乘

    2.快速幂求阶乘

    ax≡1(mod  p),我们知道a与p互质,根据费马小定理,我们知道a的逆元就是a^(p-2)

    那么一个快速幂就可以解决,但是如果p太大,这个算法就。。。

    (仍然是上面那道题的代码)

    #include<bits/stdc++.h>
    #define maxn 100005
    #define ll long long
    using namespace std;
    ll fac[maxn];
    ll quick_pow(ll a,ll x,ll mod)
    {
        ll ans=1,t=a;
        while(x)
        {
            if(x%2==1) ans=ans*t%mod;
            t=t*t%mod;
            x=x>>1%mod;
        }
        return ans%mod;
    }
    ll C(int n,int m,int p)
    {
        return fac[n]*quick_pow(fac[n-m],p-2,p)%p*quick_pow(fac[m],p-2,p)%p;
    }
    int main()
    {
        int T;
        scanf("%d",&T);
        while(T--)
        {
            int n,m,p;
            scanf("%d%d%d",&n,&m,&p);
            fac[0]=fac[1]=1;
            //for(int i=2;i<=m+n;i++)
              //invfac[i]=-p/i*invfac[p%i]%p;
            for(int i=2;i<=m+n;i++) 
            {
                fac[i]=fac[i-1]*i%p;
            
            }
            printf("%I64d
    ",C(m+n-1,n-1,p));
        }
    }
    快速幂逆元

    3.扩欧求逆元

    ax≡1(mod  p),也就是ax-py=1 

    这个算法的好处是不用保证p一定是个质数,只要与a互质即可

    (仍然是上面那道题的代码)

    #include<bits/stdc++.h>
    #define maxn 100005
    #define ll long long
    using namespace std;
    ll exgcd(ll a,ll &x,ll b,ll &y)
    {
        if(b==0){x=1;y=0;return a;}
        ll x2,y2;
        ll gcd=exgcd(b,x2,a%b,y2);
        x=y2;
        y=x2-a/b*y2;
        return gcd;        
    }
    ll C(int n,int m,int p)
    {
        ll mm=1,nn=1;
        for(int i=n;i>m;i--)mm=mm*i%p;
        for(int i=1;i<=n-m;i++) nn=nn*i%p;
        ll y,inv;
        ll gcd=exgcd(nn,inv,p,y);
        inv/=gcd;
        mm=(mm*inv%p+p)%p;
        return mm;
    }
    int main()
    {
        int T;
        scanf("%d",&T);
        while(T--)
        {
            int n,m,p;
            scanf("%d%d%d",&n,&m,&p);
            printf("%I64d
    ",C(m+n-1,n-1,p));
        }
    }
    扩欧逆元

    至于什么模数又不是质数又不和a互质,好像是还要开个数组存个啥啥的,emm现在我还不会la

  • 相关阅读:
    vue项目中引用spreadjs方法
    使用Element的table合并单元格的问题(合并行)
    卸载mysql
    mac 下安装mySQL
    react中界面跳转 A界面跳B界面,返回A界面,A界面状态保持不变 localStorage方法
    react点击弹出带出值和点击跳转页面带出值
    ajax优缺点
    vue项目console.log报错
    性能优化
    对象生命周期
  • 原文地址:https://www.cnblogs.com/yyys-/p/10501881.html
Copyright © 2011-2022 走看看