1. 程式人生 > >淺談逆元 (高中OJ3805)

淺談逆元 (高中OJ3805)

先看一道題目:

  1. 【NOIP2014模擬8.24】小X 的二叉堆計數 (Standard IO)
    Time Limits: 1000 ms Memory Limits: 262144 KB Detailed Limits
    Description
    眾所周知,完全二叉樹是一種二叉樹,滿足除最後一層外的每層結點都是滿的,且最後一層的結點連續集中在左方。而二叉堆是一種完全二叉樹,分為大根堆和小根堆,大根堆滿足父結點的值不小於子結點的值,小根堆滿足父結點的值不大於子結點的值。
    小X 最近對二叉堆和樹的計數都很感興趣,他想知道n 個互不相同的數能構成多少個不同的大小為n 的二叉堆,希望你幫幫他。
    Input
    第一行包含一個整數n。
    Output
    第一行包含一個整數,表示能構成的二叉堆個數模10^9 + 7。
    Sample Input
    3
    Sample Output
    4
    Data Constraint
    對於30% 的資料,n ≤ 10。
    對於60% 的資料,n ≤ 1000。
    對於80% 的資料,n ≤ 10^5。
    對於100% 的資料,1 ≤ n ≤ 5 × 10^6。

對於這道題目,先講講較為容易想到的方法。
設一個方程F[i]表示有I個節點時能構建的堆的個數。
方程:F[i]=F[i.left]*F[i.right]*C(i-1,i.left)
i.left和i.right分別表示在節點數為i的情況下完全二叉樹的左右子樹節點個數。
同時為了方便計算,可以先把要用到的節點處理一下,之後就只用計算這些節點。

BUT——
這個方法最大的問題就是求組合數。
看看組合數的公式:
N!
C(N,M)=————–
M!(N-M)!
因為算出來的數要MOD 10^9+7,所以我們只能把上下兩個數分解,再互相抵消,最後邊乘邊mod。

設被除數和除數為a,b,這個公式應該是這樣:
C(N,M)=a/b mod md (md=10^9+7)

思考:如何優化?
因為邊乘邊MOD,最後一起除會錯,那為什麼不可以把除法轉成乘法?
得出:
a*(1/b) mod md
a我們很容易算,重點是算b。

再引入一個概念:
費馬小定理:b^(c-1) mod c=1 (b為質數)
把式子左右兩邊同除b,得
b^(c-2) mod c=1/b

把得出來的式子代回去,得:(無視a)
b^(md-2) mod md
而10^9+7剛好又是一個質數,所以可以滿足這個條件。

再把b拆開:
(M!(N-M)! mod md)^(md-2) mod md
現在就很好算了。

總結:

費馬小定理:a^(c-1) mod c=1
得a^(c-2) mod c=1/c
把式子往回代,再用快速冪取模。
總之就是要仔細觀察題目,多看幾遍自然就明白了。