組合數的計算方法 1
考慮用 \(\operatorname{DP}\) 求解,設 \(f_{i,j}\) 表示 \(i \choose j\) 那么可以得出 \(\operatorname{DP}\) 方程為 \(f_{i,j}=f_{i-1,j-1}+f_{i-1,j}\)(其中 \(f_{i-1,j-1}\) 表示新增加的這個數要選,\(f_{i-1,j}\) 表示新增加的數不選)。
預處理的時間復雜度 \(\mathcal{O}(n^2)\),單次查詢的復雜度為 \(\mathcal{O}(1)\)。
因為方法 \(1\) 的轉移中只有簡單的加減,所以對模數並沒有什么限制,但是復雜度略高,難以處理數據較大時的情況。
組合數的計算方法 2
考慮預處理出階乘以及階乘的逆元,這樣可以直接使用 \({n \choose m}=\frac{n!}{m!(n-m)!}\) 得到答案。
預處理的時間復雜度可以做到 \(\mathcal{O}(n)\),單次查詢的復雜度為 \(\mathcal{O}(1)\)。
因為方法 \(2\) 需要處理逆元,所以必須滿足逆元的存在。
組合數的計算方法 3
當 \(n,m\) 非常大時,考慮用 \(\mathcal{L}ucas\) 求解,下面介紹一下什么是 \(\mathcal{L}ucas\) 定理。
先介紹一個輔助工具多項式下的同余:
如果兩個存在兩個多項式 \(f=\sum\limits_{i=0}^{n}a_ix^i,g=\sum\limits_{i=0}^{n}b_ix^i\) 滿足對於任意的 \(i\in[0,n]\) 都有 \(a_i\equiv b_i\pmod p\),那么就稱 \(f\) 和 \(g\) 在模 \(p\) 意義下同余,即 \(f\equiv g\pmod p\)。
除此之外,如果 \(p\) 是質數,那么對於任意的 \(i\in(0,p)\) 都有
所以可以得出:
(其中 \(p\) 是質數)
現在我們要計算 \((1+x)^n\),先把 \(n\) 轉成 \(p\) 進制(假設 \(n\) 在 \(p\) 進制下位 \(n_0n_1n_2\dots\),即 \(n=\sum n_ip^i\)),因為
所以可以得出
可以發現這些部分的卷積在任何一個位置恰好只有在一個位置對答案產生影響,即在 \(m\) 在 \(p\) 進制下的對應位置有貢獻,所以可以得到
(假設 \(m\) 在 \(p\) 進制下位 \(m_0m_1m_2\dots\))
預處理復雜度為 \(\mathcal{O}(p)\),單次查詢的復雜度為 \(\mathcal{O}(\log_{(p)}n)\)。
方法 \(3\) 需要處理出 \(0\sim p-1\) 的數的階乘,所以通常情況下 \(p\) 不可以很大。
組合數的計算方法 4
考慮方法 \(2\) 預處理的復雜度至少為 \(\mathcal{O}(n)\),如果 \(n,m\) 比較大時(\(n,m\) 在 \(10^9\) 級別左右),\(\mathcal{O}(n)\) 的復雜度就難以接受。可以發現方法 \(2\) 的瓶頸在於計算階乘,於是有了一種非常暴力的處理方法:
考慮先利用另一個程序計算出 \(10^9\) 內的階乘,設定一個閾值 \(W\),將 \(W\) 的倍數的階乘放入一個數組中(\(W\) 大約在 \(10^5\sim 10^6\) 內比較適合),這樣我們可以得到一份大小可以接受的表,那么計算一個階乘只需要找到最接近的一個在數組中的數再繼續往上乘,那么單次計算的復雜度上界為 \(\mathcal{O}(W)\)。我們稱這種方法為分塊打表。
方法 \(4\) 同樣也存在着方法 \(2\) 的弊病,而且模數不可以改變,單次計算復雜度較高(如果想要改變模數可以使用快速階乘算法)。
組合數的計算方法 5
考慮到普通的 \(\mathcal{L}ucas\) 的預處理的復雜度至少為 \(\mathcal{O}(p)\),但瓶頸也在於計算階乘,自然也是可以用分塊打表來解決。
這樣做單次查詢的復雜度為 \(\mathcal{O}(W\log_{(p)}n)\),因為 \(p\) 一般很大,所以其實並不會很慢。
這樣我們就得到一個可以計算 \(n,m\) 在 \(10^{18}\) 級別,模數在 \(10^9\) 級別的計算組合數的優秀做法。
組合數的計算方法 6
考慮最暴力的任意模數做法,因為要任意模數所以不能存在模意義下的除法,考慮直接對每個數分解質因數,利用歐拉篩曬出最小質因子后可以做到 \(\mathcal{O}(n\log n)\) 的復雜度完成對於 \(1\sim n\) 中的每一個質數出現次數的統計。
不過這樣計算的復雜度還是有點高了,這里給出一種更優的做法。
考慮到 \(1\sim n\) 中的素數個數為 \(\mathcal{O}(\frac{n}{\log n})\) 級別。所以可以只枚舉每個 \(1\sim n\) 中的質數再計算每種質數的貢獻。
至於計算每個質數 \(p\) 的貢獻顯然可以輕松得到:
(\(num\) 為 \(p\) 這個質數在 \(1\sim n\) 質因數分解后出現次數)
最后計算所有質數的貢獻的乘積時只需要快速冪即可,經過簡單分析可以得出這個做法的復雜度上界是 \(\mathcal{O}(n)\) 的。
組合數的計算方法 7
當 \(n,m\) 很大且 \(p\) 不一定是質數,就不能簡單套用的 \(\mathcal{L}ucas\)。於是可以考慮用 \(\operatorname{Ex}\mathcal{L}ucas\) 解決。下面介紹一下 \(\operatorname{Ex}\mathcal{L}ucas\)。
因為 \(p\) 不一定是質數,所以考慮先對 \(p\) 質因數分解(\(p\) 質因數分解后 \(p_0^{k_0}p_1^{k_1}\dots\))
現在要計算 \({n\choose m}\bmod p\),顯然可以先計算出所有的 \({n\choose m}\bmod {p_i^{k_i}}\)(因為對於每個 \(i\) 的計算方法都一樣,所以下面稱 \(p_i\) 為 \(p\),\(k_i\) 為 \(k\)),再用 \(\operatorname{CRT}\) 合並即可。
還是回歸本源 \({n\choose m}=\frac{n!}{m!(n-m)!}\),所以現在要求的是 \(\frac{n!}{m!(n-m)!}\bmod p^x\),因為要計算除法,但是除數與模數不互質的情況下是不存在逆元的,所以可以考慮先將除數和被除數中的 \(p\) 都提出。
設 \(F_p(n)\) 表示 \(\frac{n!}{p^x}\)(其中 \(x\) 為滿足 \(F_p(n)\) 為自然數時最大的自然數),用 \(G_p(n)\) 表示這里的 \(x\)。也就是說 \(n!=F_p(n)\times p^{G_p(n)}\)。
可以得出:
並且根據定義可以得出 \(F_p(n)\not\equiv 0\pmod p\),因為 \(p\) 這里是質數,所以可以得到 \(p\) 與 \(F_p(n)\) 互質,\(p^k\) 自然也和 \(F_p(n)\) 互質。這樣我們就可以愉快地去計算逆元啦。
下面來考慮 \(F_p(n)\) 和 \(G_p(n)\) 如何計算:
先考慮 \(G_p(n)\),可以發現這個東西在方法 \(6\) 中已經計算過了:
再來考慮 \(F_p(n)\) 的計算:
先把 \(1\sim n\) 中 \(p\) 的倍數拎出來不考慮(下面假設 \(p=5\)),那么剩下部分為:
把每 \(4\) 個分為一組,那么每一組都可以都表示為 \((5k+1)\times(5k+2)\times(5k+3)\times(5k+4)\ (k\in \mathbb{Z}_*)\),那么顯然對於每一組都是在模 \(5\) 意義下同余的。
所以我們可以得到:
可以發現提出的數都是 \(5k\ (k\in \mathbb{Z}_*)\),把 \(5\) 除掉后又變成了 \(1,2,3,\dots\),於是又變成了相同的問題,所以可以直接遞歸求解。
綜合以上內容可以得出以下式子:
但是這里要求的是 \(F_p(n)\bmod p^k\),所以需要將上面得到的這個式子稍微變形以下得到下面式子:
其中 \(R_p(n)\) 是在階乘中除去 \(p\) 的倍數,即 \(\frac{n!}{\prod_{i=1}^{\lfloor\frac{n}{p}\rfloor}pi}\),可以在預處理的時候直接處理出來。
可能不能一眼看出這連個式子的不同之處,但是在計算 \(F_2(3)\bmod 2^2\) 時就可以發現如果直接套用第一個式子會得到的是 \(1\) 而不是 \(3\)。
需要用到 \(\operatorname{Ex}\mathcal{L}ucas\) 的題目一般都會對 \(\max\{p_i^{k_i}\}\) 的范圍有規定,因為需要處理出所有的 \(p_i^{k_i}\) 以內的階乘模 \(p_i^{k_i}\) 下的結果。
預處理的復雜度 \(\mathcal{O}(\sum p_i^{k_i})\),單次詢問的時間復雜度 \(\mathcal{O}(\log^3n)\),如果在計算 \(F_p(n)\bmod p^k\) 時通過光速冪優化其中冪的計算可以做到 \(\mathcal{O}(\log_2n)\)。
組合數的計算方法 ∞
總所周知,組合數又稱二項式系數,所以計算 \(n\choose m\) 其實就是計算 \((1+x)^n\) 中 \(x^m\) 的系數,所以在 \(m\) 較小的時候可以用多項式快速冪輕松解決。
計算出 \({n\choose 0},{n\choose 1},\dots,{n\choose m-1},{n\choose m}\) 的值的復雜度為 \(\mathcal{O}(m\log m)\)。