卢卡斯定理&扩展卢卡斯(Lucas)

卢卡斯定理&拓展卢卡斯

部分资料来自:

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;
}

猜你喜欢

转载自www.cnblogs.com/et3-tsy/p/12499280.html