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;
}
这是我之前在洛谷博客的第一篇文章。