(扩展)Lucas定理——求大组合数取模

Lucas定理

P 质数

C n r C n / P r / P × C n   m o d   P r   m o d   P   ( m o d   P )

要用生成函数证明(不会,所以不证了)

实现直接递归:

void exgcd(long long a,long long b,long long &x,long long &y)
{
    if(b==0)
        x=1,y=0;
    else
    {
        long long x1,y1;
        exgcd(b,a%b,x1,y1);
        x=y1;
        y=x1-(a/b)*y1;
    }
}
long long inv(long long x,long long p)
{
    long long i,t;
    exgcd(x,p,i,t);
    i=(i%p+p)%p;
    return i;
}


long long C(long long n,long long r,long long MOD)
{
    if(n<r)
        return 0;
    if(r==0)
        return 1;
    long long ans=1LL;
    ans=C(n/MOD,r/MOD,MOD);
    n%=MOD,r%=MOD;
    long long a=1,b=1;
    for(int i=n;i>n-r;i--)
        a=(a*i)%MOD;
    for(int i=1;i<=r;i++)
        b=(b*i)%MOD;
    ans=(ans*a%MOD*inv(b,MOD))%MOD;
    return ans;
}

扩展Lucas定理

可以解决模数 P 不是质数的组合数取模

P 质因数分解: P = p 1 k 1 p 2 k 2 . . . p t k t
如果我们求出一下方程中的 a 1 , a 2 . . . a t ,就可以利用中国剩余定理求出 C n r

{ C n r a 1   ( m o d   p 1 k 1 ) C n r a 2   ( m o d   p 2 k 2 ) C n r a 3   ( m o d   p 3 k 3 ) . . . C n r a t   ( m o d   p t k t )

现在来求 C n r a   ( m o d   p k )

C n r n ! ( n r ) !   r !   ( m o d   p k )

a n !   ( m o d   p k ) b r !   ( m o d   p k ) c ( n r ) !   ( m o d   p k )

将组合数分成三个阶乘计算: C n r a   b 1 c 1   ( m o d   p k )
b 1 表示逆元)
但此时如果 b p k 不互质,就无法求出逆元,所以需要把 a , b , c 中的质因子 p 全部提出来,求了逆元之后再乘回去。

举个例子:计算 19 !   ( m o d   3 2 )

19 ! = 1 × 2 × 3 × 4 × . . . × 19 = 1 × 2 × 4 × 5 × 7 × 8 × 10 × 11 × 13 × 14 × 16 × 17 × 19 × 3 6 × ( 1 × 2 × 3 × 4 × 5 × 6 )

即把阶乘中的 p 质因子提出来,后面 6 ! 继续递归求解,前面第一排的式子有周期性的:
1 × 2 × 4 × 5 × 7 × 8 10 × 11 × 13 × 14 × 16 × 17   ( m o d   3 2 )

所以暴力求出一个区间的值 t ,结果乘 t 19 9

细节见代码:

long long pow_mod(long long a,long long b,long long p)
{
    long long res=1;
    while(b)
    {
        if(b&1LL)
            res=(res*a)%p;
        b>>=1LL;
        a=(a*a)%p;
    }
    return res;
}
void exgcd(long long a,long long b,long long &x,long long &y)
{
    if(b==0)
    {
        x=1,y=0;
        return;
    }
    exgcd(b,a%b,y,x);
    y-=(a/b)*x;
}
long long inv(long long a,long long p)//逆元必须用扩展欧几里得,因为p不为质数
{
    long long i,t;
    exgcd(a,p,i,t);
    i=(i%p+p)%p;
    return i;
}

long long Fac(long long N,long long p,long long pk)
{
    if(N==0)
        return 1;
    long long ans=1;
    if(N>=pk)//周期性
    {
        for(long long i=1;i<pk;i++)
            if(i%p)
                ans=(ans*i)%pk;
        ans=pow_mod(ans,N/pk,pk);
    }
    for(long long i=1;i<=N%pk;i++)//除了周期性的,剩下的几个数
        if(i%p)
            ans=(ans*i)%pk;
    ans=(ans*Fac(N/p,p,pk))%pk;//递归求解
    return ans;
}
long long C(long long N,long long R,long long p,long long pk)
{
    if(R==0)
        return 1;
    long long a=Fac(N,p,pk),b=Fac(N-R,p,pk),c=Fac(R,p,pk),k=0,ans;//求出几个阶乘,不包含质因子p
    //下面单独计算质因子p的个数
    for(long long i=N;i;i/=p)
        k+=i/p;
    for(long long i=N-R;i;i/=p)
        k-=i/p;
    for(long long i=R;i;i/=p)
        k-=i/p;
    ans=a*inv(b,pk)%pk*inv(c,pk)%pk*pow_mod(p,k,pk)%pk;
    return ans;
}
long long C(long long N,long long R,long long M)
{
    if(N<R)
        return 0;
    long long ans=0,m=M,temp;
    for(long long p=2,pk;p*p<=m;p++)
        if(m%p==0)//枚举模数M的因子p
        {
            pk=1;
            while(m%p==0)
                m/=p,pk*=p;
            temp=C(N,R,p,pk);//求C(N,R)mod p^k
            ans=(ans+temp*(M/pk)%M*inv(M/pk,pk)%M)%M;//中国剩余定理
        }
    if(m!=1)
    {
        temp=C(N,R,m,m);
        ans=(ans+temp*(M/m)%M*inv(M/m,m)%M)%M;
    }
    return ans;
}

猜你喜欢

转载自blog.csdn.net/can919/article/details/79765790