卢卡斯定理&拓展卢卡斯
部分资料来自:
1.https://blog.csdn.net/qq_40679299/article/details/80489761
2.https://www.luogu.com.cn/problemnew/solution/P3807
本文章中,排列组合C(n,m)表示,n中取m个,不考虑顺序
前置芝士~:
1)乘法逆元
如果ax≡1 (mod p),且gcd(a,p)=1(a与p互质),则称a关于模p的乘法逆元为x
2)费马小定理:
a ^(p-1)≡1(mod p)(其中a与p互质)
3)扩展欧几里得
已知整数a、b,扩展欧几里得算法可以在求得a、b的最大公约数的同时,能找到整数x、y(其中一个很可能是负数),使它们满足贝祖等式ax + by = gcd(a, b)
求逆元一般可以使用费马小定理或扩展欧几里得实现。
卢卡斯定理
那么我们现在开始正式介绍lucas定理。
适用范围
所求C(n,m)%mod,mod一般是质数,并且n,m相对而言比较大,而且如果mod显然大于n,m,那就没必要写lucas,光求逆元足够了
像下面这样的取n,m,p的值就比较好地适用lucas
40118 16827 2521
18506 3902 883
核心思想
我们在组合数学当中要计算C(n,m)的时候往往数据比较大,需要进行取模操作。
由于含有除法的计算,不能直接取模,否则会出现像下面的问题:
你比如3 * 5 * 7 / 5 % 4
你如果正常计算答案是1,而如果你先算出15,取模成3,再算21,取模成1,那么还有个5没除,那怎么办?
显然,这样处理忽视了一个问题,即在前面取走的k * 4并没有被/5处理,而这一部分一旦/5后,很可能不再能被4整除
而Lucas定理正是基于此,将除法通过求逆元的方式,将其转化成乘法,使得取模操作不会产生上述的后果
F / A === 所求量 (mod C)
如果存在 A * X === 1 (mod C)
那么2边同时乘起来,得到 F * X === 所求量 (mod C)
成立条件
(1) 模方程 A * X === 1(mod C) 存在解
(2) A | F (F % A == 0)
这样除法就巧妙地变成了乘法
定理描述
冯志刚《初等数论》版及证明:
洛谷大佬证明
扩展卢卡斯定理
洛谷P3807
#include<iostream>
#include<cstring>
using namespace std;
typedef long long int LL;
LL p;
LL min(LL a,LL b)
{
return a<b?a:b;
}
LL up(LL k,LL num)
{
LL awsl=1;
while(k)
{
if(k&1)awsl=(awsl*num)%p;
k/=2;
num=(num*num)%p;
}
return awsl%p;
}
LL getx(LL a)
{
if(a==1)return 1;
return up(p-2,a);
}
LL c(LL m,LL n)
{
if(m<n)return 0;
n=min(n,m-n);
LL ans=1;
for(int i=1;i<=n;i++)
{
ans=ans*(m+1-i)%p*getx(i)%p;
}
return ans;
}
LL Lucas(LL m,LL n)
{
LL ans=1;
LL cur=1;
while(cur<=m||cur<=n)cur*=p;
while(cur)
{
ans*=c(m/cur,n/cur);
m=m%cur;
n=n%cur;
cur/=p;
}
return ans%p;
}
void solve()
{
LL n,m;
cin>>n>>m>>p;
cout<<Lucas(m+n,n)<<endl;
}
int main()
{
int t;
cin>>t;
for(int i=0;i<t;i++)
{
solve();
}
return 0;
}