C++求解组合数

递推法 杨辉三角

请添加图片描述
递推式 C n m = C n − 1 m + C n − 1 m − 1 C_{n}^{m} = C_{n-1}^{m} + C_{n-1}^{m-1} Cnm=Cn1m+Cn1m1
从 n个不同的数中选出 m个的方案数是 C n m C_{n}^{m} Cnm
对第 1 个数有 选或不选 两种决策: C n m C_{n}^{m} Cnm
若不选,则从剩下的 n−1个中选 m 个,即 ​; C n − 1 m C_{n-1}^{m} Cn1m
若选,则从剩下的 n−1个中选 m−1 个,即​。 C n − 1 m − 1 C_{n-1}^{m-1} Cn1m1
加法原理 C n m = C n − 1 m + C n − 1 m − 1 C_{n}^{m} = C_{n-1}^{m} + C_{n-1}^{m-1} Cnm=Cn1m+Cn1m1
请添加图片描述

void comb()
{
    for(int i=0;i<n;i++)
    {
        for(int j=0;j<=i;j++)
        {
            if(j==0)C[i][j] = 1;
            else C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod;
        }
    }
}

快速幂+逆元

请添加图片描述

组合数计算优化方案

递推法 C n m = C n − 1 m + C n − 1 m − 1 C_n^m = C_{n-1}^m + C_{n-1}^{m-1} Cnm=Cn1m+Cn1m1,会 TLE。
考虑用 C n m = n ! ( n − m ) ! ⋅ m ! C_n^m = \dfrac{n!}{(n - m)! \cdot m!} Cnm=(nm)!m!n! 直接计算。
开两个数组分别存模意义下的阶乘和阶乘的逆元:

  • f [ x ] f[x] f[x] x !   m o d   p x! \bmod p x!modp 的值;
  • g [ x ] g[x] g[x] ( x ! ) − 1   m o d   p (x!)^{-1} \bmod p (x!)1modp 的值。
    因为 p p p 是质数且 n , m n, m n,m 都小于 p p p,即 n , m n, m n,m p p p 互质,
    所以根据费马小定理 a ⋅ a p − 2 ≡ 1 ( m o d p ) a \cdot a^{p - 2} \equiv 1 \pmod p aap21(modp)
    因为 a ⋅ a − 1 ≡ 1 ( m o d p ) a \cdot a^{-1} \equiv 1 \pmod p aa11(modp)
    所以 a − 1 ≡ a p − 2 ( m o d p ) a^{-1} \equiv a^{p-2} \pmod p a1ap2(modp)
    可以用快速幂求逆元。
    无法直接计算 n ! ( n − m ) ! ⋅ m !   m o d   p \dfrac{n!}{(n - m)! \cdot m!}\bmod p (nm)!m!n!modp,所以转化为 n ! ⋅ ( ( n − m ) ! ⋅ m ! ) − 1   m o d   p n!\cdot((n-m)!\cdot m!)^{-1} \bmod p n!((nm)!m!)1modp
    所以 C n m   m o d   p = f [ n ] × g [ n − m ] × g [ m ]   m o d   p C_n^m \bmod p = f[n] \times g[n - m] \times g[m] \bmod p Cnmmodp=f[n]×g[nm]×g[m]modp
    请添加图片描述
typedef long long ll;
ll qpow(ll a,int b,ll p)
{
   ll res;
   while(b)
   {
    if(a & 1)res=res*a%p;
    a=a*a%p;
    b >>= 1;
   }
   return res;
}
void init()
{
    f[0]=1,g[0]=1;
    for(int i=1;i<n;i++)
    {
        f[i] = i*f[i-1]%p;
        g[i] = g[i-1]*qpow(i,p-2)%p;
    }
}
ll comb(ll n,ll m)
{
    return f[n]*g[n-m]%p*g[m]%p;
}

卢卡斯定理

请添加图片描述
卢卡斯定理:
C n m ≡ C n / p m / p ⋅ C n   m o d   p m   m o d   p ( m o d p ) C_n^m \equiv C_{n/p}^{m/p} \cdot C_{n \bmod p}^{m \bmod p} \pmod{p} CnmCn/pm/pCnmodpmmodp(modp)
其中 p p p 为质数。
n   m o d   p n \bmod p nmodp m   m o d   p m \bmod p mmodp 一定是小于 p p p 的数,可以直接求解,
C n / p m / p C_{n/p}^{m/p} Cn/pm/p 可以继续用 Lucas 定理求解。
边界条件:当 m = 0 m = 0 m=0 时,返回 1 1 1

typedef long long ll;
ll qpow(ll a,int b,ll p)
{
   ll res;
   while(b)
   {
    if(a & 1)res=res*a%p;
    a=a*a%p;
    b >>= 1;
   }
   return res;
}
void init(ll N,ll p)
{
    f[0]=1,g[0]=1;
    for(int i=1;i<N;i++)
    {
        f[i] = i*f[i-1]%p;
        g[i] = g[i-1]*qpow(i,p-2)%p;
    }
}
ll comb(ll n,ll m,ll p)
{
    return f[n]*g[n-m]%p*g[m]%p;
}
ll lucas(ll n,ll m)
{
    if(m==0)return 1;
    return lucas(n/p,m/p)*comb(n%p,m%p)%p;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值