加载中...

组合计数

组合数

\(C^m_n\) = \(\dfrac{n (n-1) (n-2) \cdots (n-m+1)}{m!}\) = \(\dfrac{n!}{m! (n-m)!}\)

\(C^m_n\) = \(C^m_{n-1}\) +\(C^{m-1}_{n-1}\)



递推法求组合数

求组合数Ⅰ \(\quad\) \(C^m_n\) = \(C^m_{n-1}\) +\(C^{m-1}_{n-1}\)

//递推法求组合数模板

//c[a][b]存储C^a_b的方案数
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];



预处理逆元求组合数

首先预处理出所有阶乘取模的余数 fact[N] , 以及所有阶乘取模的逆元 infact[N]

如果取模的数是质数, 可以用费马小定理求逆元


求组合数Ⅱ \(\quad\) \(C^m_n\) = \(\dfrac{n!}{m!(n-m)!}\)

//快速幂模板
int qmi(int a,int k,int p)
{
    int res=1;
    while(k)
    {
        if(k&1)res=(long long)res*a%p;
        a=a*a%p;
        k>>=1;
    }
    return res;
}

//预处理阶乘的余数和阶乘余数的逆元
fact[0]=infact[0]=1;
for(int i=1;i<N;i++)
{
    fact[i]=(long long)fact[i-1]*i%mod;
    infact[i]=(long long)infact[i-1]*qmi(i,mod-2,mod)%mod;
}



Lucas定理

Lucas定理: 若p是质数, 则对于任意整数 1 \(\le\) m \(\le\) n , 有:

\(C^m_n\) \(\equiv\) \(C^{m\%p}_{n\%p}\) \(\cdot\) \(C^{m/p}_{n/p}\) (mod p)


求组合数Ⅲ \(\quad\) \(C^m_n\) \(\equiv\) \(C^{m\%p}_{n\%p}\) \(\cdot\) \(C^{m/p}_{n/p}\) (mod p)

//快速幂模板
int qmi(int a,int k,int p)
{
    int res=1;
    while(k)
    {
        if(k&1)(long long)res=res*a%p;
        a=(long long)a*a%p;
        k>>=1;
    }
    return res;
}

//通过定理求组合数C^a_b
int C(int a,int b,int p)
{
    if(a<b)return 0;
    
    long long x=1,y=1;	//x是分子,y是分母
    for(int i=a,j=1;j<=b;i--,j++)
    {
        x=(long long)x*i%p;
        y=(long long)y*j%p;
    }
    
    return x*(long long)qmi(y,p-2,p)%p;
}

int Lucas(long long a,long long b,int p)
{
    if(a<p&&b<p)return C(a,b,p);
    return (long long)C(a%p,b%p,p)*Lucas(a/p,b/p,p)%p;
}



分解质因数法求组合数

当我们需要求出组合数的真实值, 而非对某个数的余数时, 分解质因数的方式比较好用:

① 筛法求出范围内的所有质数

② 通过 \(C^b_a\) = \(\dfrac{a!}{b!(a-b)!}\) 这个公式求出每个质因子的次数

\(\quad\) n ! 中 p 的次数是 [ \(\dfrac{n}{p}\) ] + [ \(\dfrac{n}{p^2}\) ] + \(\cdots\)

③ 用高精度乘法将所有质因子相乘


求组合数Ⅳ

int primes[N],cnt;	//存储所有质数
int sum[N];		//存储每个质数的次数
bool st[N];		//存储每个数是否已被筛掉

void get_primes(int n)	//线性筛法求素数
{
    for(int i=2;i<=n;i++)
    {
        if(!st[i])primes[cnt++]=i;
        for(int j=0;primes[j]<=n/i;j++)
        {
            st[primes[j]*i]=true;
            if(i%primes[j]==0)break;
        }
    }
}

int get(int n,int p)	//求n!中p的次数
{
    int res=0;
    while(n)
    {
        res+=n/p;
        n/=p;
    }
    return res;
}

vector<int> mul (vector<int>a, int b)	//高精度乘低精度模板
{
    vector<int>c;
    int t=0;
    for(int i=0;i<a.size();i++)
    {
        t+=a[i]*b;
        c.push_back(t%10);
        t/=10;
    }
    while(t)
    {
        c.push_back(t%10);
        t/=10;
    }
    return c;
}

get_primes(a);	//预处理范围内的所有质数

for(int i=0;i<cnt;i++)	//求每个质因数的次数
{
    int p=primes[i];
    sum[i]=get(a,p)-get(b,p)-get(a-b,p);
}

vector<int>res;
res.push_back(1);

for(int i=0;i<cnt;i++)		//用高精度乘法将所有质因子相乘
    for(int j=0;j<sum[i];j++)
        res=mul(res,primes[i]);



卡特兰数

给定n个0和n个1, 它们按照某种顺序排成长度为2n的序列, 满足任意前缀中0的个数都不少于1的个数的序列的数量为:

Cat (n) = \(C^n_{2n}\) - \(C^{n-1}_{2n}\) = \(\dfrac{C^n_{2n}}{n+1}\)



posted @ 2023-04-29 11:42  邪童  阅读(53)  评论(0编辑  收藏  举报