试题
题目描述
给出 n 和 k,求
min
{
n
!
k
!
(
n
−
k
)
!
,
1
0
18
}
\displaystyle \min\left\{\frac{n!}{k! (n - k)!}, 10^{18}\right\}
min{k!(n−k)!n!,1018} 的值。
其中
n
!
=
1
×
2
×
⋯
×
n
n! = 1 \times 2 \times \cdots \times n
n!=1×2×⋯×n, 表示 n 的阶乘。
输入描述
输入文件包含多组数据,请处理到文件结束。
每组数据包含两个整数 n 和 k。
- 0 ⩽ k ⩽ n ⩽ 1 0 9 0 \leqslant k \leqslant n \leqslant 10^9 0⩽k⩽n⩽109
- 至多 1 0 5 10^5 105 组数据。
输出描述
对于每组数据,输出一个整数,表示所求的值。
样例输入
1000000000 0
1000000000 2
1000000000 500000000
样例输出
1
499999999500000000
1000000000000000000
解题思路
首先,根据杨辉三角形,可知
C
m
n
=
C
m
m
−
n
C_m^n = C_m^{m-n}
Cmn=Cmm−n
所以, C 10000 9001 \displaystyle C_{10000}^{9001} C100009001 可以弄成 C 10000 999 \displaystyle C_{10000}^{999} C10000999 来算,这样子就快多了。
然后,我们注意到组合数公式:
C
m
n
=
A
m
n
n
!
=
m
!
(
m
−
n
)
!
n
!
C_m^n = \frac{A_m^n}{n!} = \frac{m!}{(m-n)!n!}
Cmn=n!Amn=(m−n)!n!m!
再看排列数公式:
A
n
m
=
m
!
(
m
−
n
)
!
=
m
∙
(
m
−
1
)
∙
(
m
−
2
)
∙
⋯
∙
(
m
−
n
+
1
)
A_n^m = \frac{m!}{(m-n)!} = m \bullet (m-1) \bullet (m-2) \bullet \cdots \bullet (m-n+1)
Anm=(m−n)!m!=m∙(m−1)∙(m−2)∙⋯∙(m−n+1)
例如,
A
9
4
=
9
×
8
×
7
×
6
A_9^4 = 9 \times 8 \times 7 \times 6
A94=9×8×7×6
对比以上两个式子,发现了什么问题呢?
对,组合数公式实际上是可以化简分步进行的。
C m n = m ∙ ( m − 1 ) ∙ ( m − 2 ) ∙ ⋯ ∙ ( m − n + 1 ) n ∙ ( n − 1 ) ∙ ( n − 2 ) ∙ ⋯ ∙ 1 = m 1 ∙ m − 2 2 ∙ m − 3 3 ∙ ⋯ ∙ m − n + 1 n \begin{array}{rcl} C_m^n & = & \displaystyle \frac{m \bullet (m-1) \bullet (m-2) \bullet \cdots \bullet (m-n+1)}{n \bullet (n-1) \bullet (n-2) \bullet \cdots \bullet 1} \\\\ & = & \displaystyle \frac{m}{1} \bullet \frac{m-2}{2} \bullet \frac{m-3}{3} \bullet \cdots \bullet \frac{m-n+1}{n} \end{array} Cmn==n∙(n−1)∙(n−2)∙⋯∙1m∙(m−1)∙(m−2)∙⋯∙(m−n+1)1m∙2m−2∙3m−3∙⋯∙nm−n+1
例如,
C 9 4 = 9 1 × 8 2 × 7 3 × 6 4 C_9^4 = \frac{9}{1} \times \frac{8}{2} \times \frac{7}{3} \times \frac{6}{4} C94=19×28×37×46
这个计算可以循环,也就是
- result 赋值为1
- 第一轮循环,result 乘上 9 1 \dfrac{9}{1} 19
- 第二轮循环,result 乘上 8 2 \dfrac{8}{2} 28
- 第三轮循环,result 乘上 7 3 \dfrac{7}{3} 37
- 第四轮循环,result 乘上 6 4 \dfrac{6}{4} 46
那么现在,应该知道怎么编写 C m n C_m^n Cmn 的算法了。
源代码
#include <cstdio>
const long long upper_limit = 1000000000000000000LL;
long long C(long long m, long long n)
{
if (n == 0 || n == m)
return 1;
if (n > m / 2)
n = m - n;
__int128 res = (__int128)1;
for (long long i = 1; i <= n; ++i)
{
res = res * (m-i+1) / i;
if (res > upper_limit)
return upper_limit;
}
return (long long)res;
}
int main()
{
long long n, k;
while (scanf("%lld%lld", &n, &k) != EOF)
{
printf("%lld\n", C(n, k));
}
return 0;
}