lucas定理(模板题题解)

题目很简单,很暴力,就是组合数,没有其他的。

但是直接暴力会炸wow

我们可以利用Lucas定理来分解字问题。

Lucas定理:C(n,m)(mod p)=C(n%p,m%p)*C(n/p,m/p)(mod p);

所以,我们可以把这个题目分解成子问题:

C(n,m+n)(mod p)=C(n%p,m+n%p)*C(n/p,(m+n)/p);

而第二个C又可以用Lucas定理求,

所以可以递归求解了

当m=0时,Lucas返回1(C(n,0)=1)

但是,还是要注意:

这题要逆元!!!

这题要逆元!!!

这题要逆元!!!

因为组合要除法,所以需要逆元。

还好题目亲切,p一定是质数,所以直接费马小搞定了。

于是:O(n)处理阶乘,logn处理逆元(卡速米)lognLucas

所以整体复杂度应该是O(Tnlog^2n )(应该不太对)

贴代码,有些注意事项在代码里写出来

#include<bits/stdc++.h>
using namespace std;
const int maxn=3e5+10;
long long n,m,p;
long long ksm(long long a,long long b)
{
    a%=p;
    long long res=1;
    while(b)
    {
        if(b%2==1)
        res=(res*a)%p;
        a=(a*a)%p;
        b>>=1;
    }
    return res;
}
long long fac[maxn];
long long C(long long a,long long b)
{
    if(a<b)//坑点,如果总数小于方案数(C(n,m)n<m,也就是选不出来)要跳出
    return 0;
    return (fac[a]*ksm(fac[b],p-2))%p*ksm(fac[a-b],p-2)%p;//逆元乘法,求组合数
}

long long lucas(long long a,long long b)
{
    if(b==0)
    return 1;
    return lucas(a/p,b/p)*C(a%p,b%p)%p;//Lucas表达
}

int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%lld%lld%lld",&n,&m,&p);
        fac[0]=1;//阶乘
        for(long long i=1;i<=p;i++)
        {
            fac[i]=(fac[i-1]*i)%p;//阶乘
        }
        printf("%lld
",lucas(n+m,n));//跑Lucas
    }
    return 0;
}

(完)

原文地址:https://www.cnblogs.com/ajmddzp/p/11235146.html