先看一道题目:
- 【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
把式子往回代,再用快速幂取模。
总之就是要仔细观察题目,多看几遍自然就明白了。