知識精華 組合數求模

2021-05-26 01:34:48 字數 4090 閱讀 6372

大家都在中學階段學習了組合數的定義:

這個表示的是從n個元素中選取m個元素的方案數。

(ps.組合數求模似乎只用在資訊學競賽和 acm競賽等計算機程式設計設計大賽中……,求在現實中的運用)

可以知道當n,m 取得比較大的時候,組合數可能很大很大 (天文數字?無法度量?)

例如 c(100, 50) = 100891344545564193334812497256, 於是計算機的 64位整數型已經沒法阻止它了!c(1000000000, 500000000) ? c( 2^50000, 2^49999 ) ? (note:這裡^表示次方,你能計算得到2的50000次級別的組合數麼?它有多少位?)

看起來似乎高精度神馬的都無法阻止這個**的函式的急速擴張了……

慶幸的是,在競賽中我們能夠遇到的規模也就只有10^9級別(顯然是mod上某個數字k,否則輸出的檔案那叫乙個大啊……),這是多麼的小呀呀呀呀!(note: 相比較2^50000 -_-)

一. 入門篇:我會暴力!

(1) k = 1: 今天你學數論了麼? 難度係數: 0

(2) (k> 1) n, m <= 1000 (n * n 是可以接受的) 難度係數: 1

遞推!c(n,m) =c(n,m – 1) + c(n – 1, m – 1)

某人: 555555 這個公式太複雜, 記憶不能!

c(5,2) = 10 = c(4,2) + c(4,1) = 6 + 4 ……

我們知道mod操作滿足加法性質,即

(a + b) mod c = ( (a mod c) + (b mod c) ) mod c

c(n,m) = ( c(n,m – 1) + c(n – 1, m – 1) ) mod k

證明利用模的定義即可……很簡單的

於是如此,我們只需要簡單的開上乙個 f[ n ][ n ],2個迴圈搞定!

其實我們遇到的大部分情況需要的 組合數 都可以用這個來搞定~

這裡唯一可能被**的其實是 k + k 溢位! 所以如果某個**的題目出到 k = 2*10^9,在某些倒霉的場合會出現2個接近k的int相加,那麼就溢位了!不要忘記用unsigned int… (我從來沒出過這種題的!真的!)

(3) n 巨大(10^9 級別), m巨小(10^4級別), k 很小,大約10^9

a) m<= 1: 今天你學數論了麼? 難度係數: 0

b) m<= 10000 難度係數: 2

可以發現分子分母的項數都少到可以接受!於是我們可以採取各種方式來通過:

i) 對於每個數字,分解素因子,合併,二分求冪! (你會數論!)

ii) 對於每個數字,只分解包含於k的素因子,例如k裡面有乙個素因子3,那麼分解的時候我只考慮3呀,因為其他部分顯然與3互質……最後統計3的次數即可……

例子:計算c(10, 3) mod 36

c(10, 3) = (10 * 9 * 8) / (1 * 2 * 3)

對於分母:

1 : ok 逆元(有區別麼?)

2: 沒法逆元, (2, 36) = 2

3: 沒法逆元, (3,36) = 3

為了神馬啊!! 還不讓人逆元啊!顯然是因為**的2和3,如果他們不存在,那麼多麼美好呀!

於是我開2個變數,記錄2,3的次數

對於分子:

10: 裡面只有1個2,去掉了2,剩下的部分是 10 / 2 = 5.

9: 裡面只有2個3,去掉去掉, 剩下的是 9 / (3^2) = 1.

8: 裡面只有3個2,去掉去掉,剩下的是 8 / (2^3) = 1

於是啊,分母我們把剩下的部分乘起來,得到了神馬?得到了 和 2,3 因子完全無關的 部分mod 36的值!就是 5 * 1 * 1 = 5了。

接下來,還有分母呢

1: 逆元(其實你可以無視它)

2: 乙個2,去掉去掉, 剩下1, 逆元繼續是1(繼續無視)

3: 乙個3,同上

接下來發現,2有幾個? 分子有4個,分母1個,所以一共只有4 – 1 = 3個

3有幾個? 同上的做法,顯然只有1個。

於是呢答案就是:

5 * 1 * 2^3 * 3^1 = 12( mod 36)

解釋:5 -> 分子除了因子2,3的積

1 ->分母除了因子2,3 的逆元的積

2^3 -> 最終統計發現有3個2

3^1 ->最終統計發現有1個3

請好好理解本例子,你會發現這個問題是如此的美妙!

經典例題:

c) m<= n 別想了!我不會!你會了教我!難度係數: -1

二. 基礎篇:我會數論!

1) n,m<= 10^6, k是10^9級別

對於n! 分解素因子,這裡就不說了,可以參加各種帖子。

之後儲存個數,二分求冪啊啊啊啊啊

2) n,m<= 10^10, k是素數,並且k 很小(比如幾百?)

其實遇到這種情況我都用乙個叫lucas定理的東西。

ni,mi 就是把 n,m分解p進製的第i位的值。

例如:計算 c(12, 4) mod 7

n = 12 (15)(base_7)

m = 4 (4) (base_7)

為了對齊,我們前面的部分補0

m = 4 (04) (base_7)

於是ans = c(5,4) * c(1,0) mod 7= 5 (mod 7)

有人又要問了, 如果mi > ni 怎麼辦呀?

直接為0!!!!!!!!!

這裡不給出證明,證明可以搜尋到。同時由於這個應用的區域比較狹窄,顯然有更簡單,更好理解的演算法,於是這裡被無視了。

三. 究極篇

n,m <= 10^9, p <= 10^5

是不是怎麼看怎麼不可做呢?

第一次見到這種題目是不是覺得作者nc了,出個不可做題 >_<

第一次交發現一坨人全部wa,是不是覺得作者的資料搞疵?!!!!

首先要知道,這題其實等價是求:

求完直接合併乙個模方程即可。(crt)

p^c 的規模大約是10^5。

c 不是1,lucas阻止不了它。

n,m太大,因子分解也阻止不了它。

下面介紹我的做法:

假設 p = 3, c = 2,也就是mod 9

假設n = 19

n! = 1 * 2 * 3 * 4 * 5 * 6 * 7 * 8 * …… * 19

要是可以快速得到 n! 中除掉3 以後 mod 9的結果,那麼多好呀!

看3多討厭,直接砍

type cal( int n) :

n! = [ 1 * 2 * 4 * 5 * 7 * 8 * … * 16 * 17 * 19 ] * (3 * 6 * 9 * 12 * 15 * 18)= [ 1 * 2 * 4 * 5 * 7 * 8 * … * 16 * 17* 19 ] * 3^6( 1 * 2 * 3 * 4 * 5 * 6)

然後發現後面的一坨實際上是 cal( n / p) !!!!

再看前半部分,尼瑪是以 p^c 為週期的啊!!!

[1 * 2 * 4 * 5 * 7 * 8 ] = [10 * 11 * 13 * 14 * 16 *17 ] = (mod 9)

於是說白了,對於前面的部分,由於週期,都是浮雲了

下面是 孤立出來的19

可以知道孤立出來的 長度 不超過 p^c ,於是暴力啊,暴力啊!

於是完美解決n! 中和 p無關的項 mod p^c的值!!!

接下來是分母部分,一模一樣,無非多了乙個求逆元(因為都和p沒關係了,逆元必然存在)

我們來分析一下,這樣的複雜度是如何的呢

每次遞迴,規模變為原來的 1/p

logp n的啊!!!

當然是層數= =

於是問題完美解決!

組合數求模

大家都在中學階段學習了組合數的定義 這個表示的是從n個元素中選取m個元素的方案數。ps.組合數求模似乎只用在資訊學競賽和 acm競賽等計算機程式設計設計大賽中 求在現實中的運用 可以知道當n,m取得比較大的時候,組合數可能很大很大 天文數字?無法度量?例如 c 100,50 100891344545...

C 求組合數 再取模

c n,m n n m m c 在處理大數除法的時候好像會出現問題,所以用除法有時候會因為精度不夠沒法得到正確答案.需要把除法變成乘法.知識儲備 逆元 快速冪 逆元 若a x m 1 我們稱x是a的逆元,x寫做a 1 相當於整除中的倒數 那假設我們要求a b m的值,當b特別大時我們無法得到正確答案...

組合數學 求組合數

對於求組合數,要根據所給資料範圍來選擇合適的演算法 這道題中所給的資料範圍適合用打表的方法直接暴力求解 先用4e6的複雜度預處理出所有的情況,再用1e4的複雜度完成詢問即可 include using namespace std const int n 2010 const int mod 1e9 ...