快速入門之——乘法逆元求組合數

逆元+快速冪+階乘求組合數(快速入門)

文章目錄

前言:

大家基本上應該都知道用楊輝三角法求組合數C(n,m)(n爲下標)
也就是直接暴力打表求法O(n*m):

for(int i=0;i<=n;i++){
	c[i][0]=c[i][i]=1;
	for(int j=1;j<i;j++)
		c[i][j]=(c[i-1][j]+c[i-1][j-1])%mod;
}

但是很明顯不好,然後就想到了這個公式:
在這裏插入圖片描述
內心獨白:對啊,直接用這個公式不就完事了嗎,但是要知道,除法是不能求餘的,而一般情況下,我們都需要進行求餘操作,即:
(a÷b)%c=(a%c÷b%c)%c
上面這種運算是錯誤的!

這就出現了今天的老大哥:逆元+快速冪+階乘求組合數

何爲逆元?

逆元就是通常所說的:倒數(這就秒懂了吧)

怎麼求逆元?

這裏得提到費馬小定理:
如果a,p互質,則:a^(p-1)≡
也就是說:a的逆元爲a^(p-2)

對於這種冪的運算,就得用到快速冪
(關於快速冪這裏不介紹)

回到主題:
在這裏插入圖片描述
這個公式目前我們就可以轉換爲:
C(n,m)=n!*inv[m!]*inv[(n-m)!]
(其中:inv表示逆元)

最後發現,階乘逆元不知道怎麼算?

怎麼求階乘逆元?

首先我們假設n!的逆元爲:inv[n!]那麼,有:inv[n*(n-1)!]=

所以就有:
inv[(n-1)!]=inv[n] * n

直接利用遞推即可。

最後關於C(n,m)的求法就大功告成了
C(n,m)=n!*inv[m!]*inv[(n-m)!]

相關代碼實現:

#include<bits/stdc++.h>
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
const int maxn=1e5+5;
const ll mod=998244353;
ll inv[maxn], fac[maxn];  //分別表示逆元和階乘
//快速冪
ll quickPow(ll a,ll b){
    ll ans=1;
    while(b){
        if(b&1)
            ans=(ans*a)%mod;
        b>>=1;
        a=(a*a)%mod;
    }
    return ans;
}

void init(){
    //求階乘
    fac[0]=1;
    for(int i=1;i<maxn;i++){
        fac[i]=fac[i-1]*i%mod;
    }
    //求逆元
    inv[maxn-1]=quickPow(fac[maxn-1],mod-2);
    for(int i=maxn-2;i>=0;i--){
        inv[i]=inv[i+1]*(i+1)%mod;
    }
}
ll C(int n,int m){
    if(m>n){
        return 0;
    }
    if(m==0)
        return 1;
    return fac[n]*inv[m]%mod*inv[n-m]%mod;
}
int main(){
    init();
    int n,m;
    scanf("%d%d",&n,&m);
    printf("%lld\n",C(n,m));
}

參考文章:
https://www.cnblogs.com/kongbursi-2292702937/p/10582258.html
https://blog.csdn.net/qq_40861916/article/details/82928080

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章