Lucas定理

P3807 【模板】Lucas定理

此为费马小定理求解逆元版本

且只讲述如何做这道题 不讲定理证明


一、前置知识

1、费马小定理

\(p\) 为质数时,
\(a^{p-1}\equiv1 \ \ \ (mod \ p)\)

$ \ $

2、乘法逆元

若有 \(ax\equiv1 \ \ \ (mod \ p)\),则称 \(x\)\(a\) 关于模 \(p\) 下的乘法逆元

(1)逆元作用

我们在在求解
$ \Large \frac{b}{a} \equiv ? \ $ \((mod \ p)\)
时,直接算$ \Large (\frac {b \bmod p}{a \bmod p})$ \(\bmod \ p\) 是错误的且有误差。

但我们做除法取模又是要高效且准确无误的算法,所以这里乘法逆元就起了作用。

我们可以设 \(x\)\(a\) 在模 \(p\) 下的乘法逆元,则

\[ \begin{cases} ax\equiv1 \ \ \ (mod \ p) \\\\ \Large\frac{b}{a} \normalsize\equiv ? \ (mod \ p) \end{cases}\]

将两式可以相乘得到

\[{b·x} \equiv \ ?·1\ (mod \ p) \]

\[{b·x \equiv \ ? \ (mod \ p)} \]

由上可知,利用乘法逆元可以避免除法取模中精确度的问题


二、求解逆元

对费马小定理及乘法逆元有理解后,我们可以轻易求解逆元了。

\[\because \ a^{p-1}\equiv1 \ \ \ (mod \ p) \]

\[\therefore \ a·a^{p-2}\equiv1 \ (mod \ p) \]

不难看出 \(a^{p-2}\) 即为 \(a\) 关于模 \(p\) 的乘法逆元。

求解逆元代码如下:

ll ksm(ll x,ll y,ll mod)//快速幂 
{
    ll ans=1;
    while(y)
    {
        if(y&1) ans*=x,ans%=mod;
        x*=x,y>>=1;
    }
    return ans;
}

ll mul_inverse(ll b,ll a,ll mod)//求b/a(mod p)
{
    return b*ksm(a,mod-2,mod)%mod;//ksm(a,mod-2,mod)是a的乘法逆元
}

三、Lucas定理

这个定理主要用于求大组合数$\begin{pmatrix}{b}\{a}\end{pmatrix} mod \ p $

定理内容:

\[\begin{pmatrix}{b}\\{a}\end{pmatrix} mod \ p = \begin{pmatrix} b/p \\ a/p\end{pmatrix} ·\begin{pmatrix} b \ \bmod p\\ a \ \bmod p\end{pmatrix} mod \ p \]

代码实现方法:递归或循环求解\(\begin{pmatrix} b/p \\ a/p\end{pmatrix} mod \ p\) 即可,求组合数时用乘法逆元

至于定理证明就问度娘吧(懒得打

四、程序代码

#include<cstdio>
#define ll long long
using namespace std;
int T;
ll n,m,p;

ll ksc(ll x,ll y,ll mod)//快速乘 
{
    ll ans=0;
    while(y)
    {
        if(y&1) ans+=x,ans%=mod;
        x<<=1,x%=mod,y>>=1;
    }
    return ans;
}

ll ksm(ll x,ll y,ll mod)//快速幂 
{
    ll ans=1;
    while(y)
    {
        if(y&1) ans=ksc(ans,x,mod),ans%=mod;
        x=ksc(x,x,mod),y>>=1;
    }
    return ans;
}

ll mul_inverse(ll x,ll y,ll mod)//求组合数对mod的取模(要用乘法逆元) 
{
    if(x<y) return 0;
    if(x==y)    return 1;//特判,不需要进行计算 
    if(y>x/2)   y=x-y;//方便计算 
    ll ans,mx=1,my=1;
    for(int i=0;i<y;i++)
    {
        mx*=(x-i),mx%=mod;
        my*=(y-i),my%=mod;
    }
    ans=mx*ksm(my,mod-2,mod)%mod;
    return ans;
}

ll lucas(ll x,ll y,ll mod)//lucas定理主体(将x和y转成mod进制) 
{
    ll ans=1;
    while(x&&y)
    {
        ans*=mul_inverse(x%mod,y%mod,mod),ans%=mod;
        x/=mod,y/=mod;
    }
    return ans;
}

int main()
{
    scanf("%d",&T);
    for(int i=1;i<=T;i++)
    {
        scanf("%lld%lld%lld",&n,&m,&p);
        printf("%lld\n",lucas(n+m,n,p));
    }
    return 0;
}

这是我之前在洛谷博客的第一篇文章。

posted @ 2022-03-24 11:36  violetctl39  阅读(35)  评论(0编辑  收藏  举报