组合数 与卡特兰数

组合数 与卡特兰数

1 a,b 比较小时

采用预处理方法,提前将所有的组合数都算出来,到时候直接查表

采用的公式是 C(a,b) = C(a-1,b) + C (a-1,b-1)

原题链接:885. 求组合数 I - AcWing题库

核心代码 :

for(int i=0;i<=2000;i++){
        for(int j=0;j<=i;j++){
            if(j==0)dp[i][j] = 1;
            else dp[i][j] = (dp[i-1][j]+dp[i-1][j-1])%mod;
         
        }
    }

2 a,b较大时

原题链接:886. 求组合数 II - AcWing题库

当a,b>=1e5时,显然已经不能直接开二维数组打表了,这样会爆数组

但是我们可以开两个一维数组,一个存取 i 的阶乘,一个存取 i 阶乘的逆元

我们可以直接从定义出发

C(a , b) = a! / b!/ (a-b)!

取模的时候,可以乘以逆元

所以C(a,b) = a! * b!^-1 * (a-b)!^-1

因为本题的m是质数,所以可以采用费马小定理。用快速幂求逆元

核心代码 :

  for(int i=1;i<=N;i++){
        ji[i] = ji[i-1]*i%mod;  //求 i 的阶乘
        ni[i] = qmi(ji[i],mod-2,mod)%mod;  //求i的阶乘的逆元
    }
  cout<<ji[a]*ni[b]%mod*ni[a-b]%mod<<endl;  //组合数定义公式

3 当a和b超级大时

当a,b>1e9时,一维的数组也存不下了,所以打表也不可以了

可以采用 卢卡斯定理

即: C(a,b) = C(a%p , b%p)* C(a/p, b/p)

原题链接:887. 求组合数 III - AcWing题库

核心代码:

//快速幂求逆元
int qmi(int a,int k){
    int res = 1;
    while(k){
        if(k&1) res = res*a%p;
        a = a*a%p;
        k>>=1;
    }
    return res;
}
//组合数定义 C(a,b) = a*(a-1)*(a-2)*...*(a-b+1) /b! 
int C(int a,int b){
        if (a<b) return 0;
    int res = 1;

    for(int i=a-b+1,j=1;i<=a;i++,j++){
        res = res*i%p;
        res = res*qmi(j,p-2)%p;
    }
    return res;
}

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

4 当最终结果不取模时

当最终结果不取模的话,随便一个组合数都能爆long long ,所以此时只能用高精度来算

因为 组合数有除法,那样我们要写一个高精度乘法,一个高精度除法

但是我们可以优化掉除法

我们可以利用分解质因数来计算一个数字的阶乘,因为a>b,所以在a的阶乘中,一定包含着b的阶乘和(a-b)的阶乘的质因子,所以我们在只需要找到这些质因子,并且把对应的次数减掉就可以了。直接只需要高精度乘法即可

a在n中的次数怎么算呢

可以这么算,n/a +n/a^2 +n/a^3 直到a^k>=n停下

为什么这么做是正确的呢 举个例子

n = 25 找出5的次数 你会发现只有 5 10 15 20 25 => 15 2 * 5 3 5 4* 5 5* 5

那么 25/5+25/ 5^2 = 6;

25的阶乘中,5的次数是6

核心代码:

vector<int> mul(int x){
    
    vector<int>a;
    int r = 0;
    for(int i=0;i<ans.size();i++){
        r = r+ans[i]*x;
     
        a.push_back(r%10);
       
        r/=10;
    }
    while(r){
        a.push_back(r%10);
        r/=10;
    } 

    return a;
}

void P(int n){
    
    for(int i=2;i<=n;i++){
        if(!st[i]){
            st[i] = true;
            prime[cnt++]  = i;
        }
        for(int j=0;prime[j]<=n/i;j++){
            st[prime[j]*i] = true;
            if(i%prime[j]==0)break;
        }
    }
}
int getci(int p,int n){
    
    int q  = p;
    int res = 0;
    while(q<=n){
        res+=(n/q);
        q*=p;
    }
    return res;
}

5 卡特兰数

直接上公式 卡特兰数 = C(2n,n)/(n+1)

你可能感兴趣的:(Acm算法,c++,算法,数据结构,c++)