大家都在中學階段學習了組合數的定義: 這個表示的是從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的啊!!! 當然是層數= = 於是問題完美解決! |