fzu2020-组合数取模-Lucas定理

【卢卡斯(Lucas)定理】

Lucas定理用来求Ca,bmod p的值,其中p为素数。

数学表达式为:

Lucas(a,b,q)=C(a%q,b%q)*Lucas(a/p,b/p,p);

Lucas(a,0,q)=0;

 

通过这个定理就可以很方便的把大数的组合转化成小数。但其中还是要求C(a%q,b%q)%p,所以这里引入逆元来求。

【定义】若整数a,b,p, 满足a·b≡1(mod p).则称a 为b 模p 的乘法逆元, 即a=b- 1mod p.其中, p 是模数。

应用到组合数中来就是:

 a!/[b!*(a-b)!] % p == a! * [b!*(a-b)!]-1 %p

【逆元求法】:

应用费马小定理,ap-1=1 mod p ,即  a*ap-2=1 mod p

也就是说  ap-2就是a的逆元。

当然这里求出来的逆元是在取模p的逆元,对我们最终目标没有影响。这也是比较方便而且比较好的方法。

【Ac Code】

#include <iostream>
#include <math.h>
using namespace std;
#define LL long long

LL fast(LL a,LL b,LL p)
{
    LL s=1;
    while(b)
    {
        if(b&1) s=(s*a)%p;
        b=b>>1;
        a=(a*a)%p;
    }
    return s;
}

LL cal(LL a,LL b,LL p)
{
    if(a<b) return 0;
    if(b>a-b) b=a-b;
    LL ans=1,coma=1,comb=1;
    for(LL i=0;i<b;i++)
        coma=(coma*(a-i))%p,comb=(comb*(b-i))%p;
    coma=coma*fast(comb,p-2,p);
    return coma%p;
}

LL Lucas(LL a,LL b,LL p)
{
    LL ans=1;
    while(a&&b&&ans)
    {
        ans=ans*cal(a%p,b%p,p);
        a=a/p;
        b=b/p;
    }
    return ans;
}

int main()
{
    LL T,a,b,p;
    cin>>T;
    while(T--)
    {
        cin>>a>>b>>p;
        cout<<Lucas(a,b,p)<<endl;
    }
    return 0;
}

借鉴了许多大牛。太感谢了~~~~~

posted on 2012-10-28 11:02  AlphaX  阅读(1307)  评论(0编辑  收藏  举报

导航