大家都在中學階段學習了組合數的定義:
這個表示的是從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 - 1,m) + 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 - 1,m) + 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 10089134454...
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 ...