[知識精華]組合數求模

來源:互聯網
上載者:User

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

 

這個表示的是從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

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

經典例題:

http://acm.fzu.edu.cn/problem.php?pid=2020

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的啊!!!

當然是層數= =

於是問題完美解決!

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.