紫书题解汇总:[紫书CH0] 《算法竞赛入门经典》(第2版) 题解目录
1. 题目来源
2. 题目说明
中文描述:
3. 题目解析
方法一:质因子分解+线性筛+唯一分解定理
这真是一道纯数学题目了,不需要知道组合数过多的结论,就题目所给的组合数定义再配合唯一分解定理就能够做该题了。我们通过式子的推导能够得到:
C
m
n
C
r
s
=
p
!
s
!
(
r
−
s
)
!
r
!
q
!
(
p
−
q
)
!
\frac {C_m^n}{C_r^s}=\frac {p!s!(r-s)!}{r!q!(p-q)!}
CrsCmn=r!q!(p−q)!p!s!(r−s)!
关于阶乘的质因子分解可参考我今天的博文,挺巧的:[数论基础] 2. 阶乘分解(暴力、因子分解、线性筛、巧妙解法)。这样我们就能得到这 6 个阶乘的所有质因子及对应的个数,类比多项式乘除法,用分子的质因子个数减去分母的质因子个数,注意需要一一对应,就能得到最终式子的质因子分解式了。最后再采用 pow
函数输出 double
类型的 5 位小数即可。在此就用 printf
控制小数位数就好了。
这个思路还是很简单的,重点考察唯一分解定理应用,及阶乘的质因子分解,关于这个是有现成的板子的。再者就是对组合数进行适当的转化。
但是这个代码足足坑了我 1 个多小时,我在本地 VS2019
下跑能过测试用例,也没报啥错误,但是放到了 oj
上就不断 wawawa
,我以为我是没考虑边界情况特殊情况,但是查了半天了没啥问题。最后,在群里问了问 qc
大佬,大佬给细心查 bug
,一些代码的小问题也指出了,原因是我的 memset(arr, 0, sizeof(arr));
没有放进 while
循环里… 我真的当时笑出来了,记得上次犯这个毛病的时候是在 牛客网,也是需要写输入输出,然后定义全局变量忘记初始化,也是苦思冥想找 bug
,最后才灵光乍现想到了这个问题。编程之路确实不好走啊,但是我也不再会怕,虚心求学,希望能多积累经验,希望有一天有足够的实力去帮助一下我能帮助到的同学,极客开源文化真是精彩迷人~~
参见代码如下:
#include <iostream>
#include <algorithm>
#include <math.h>
#include <string.h>
using namespace std;
const int MAXN = 1e5 + 50;
int arr[MAXN][6], prime[MAXN];
void init_prime() {
for (int i = 2; i < MAXN; ++i) {
if (!prime[i]) {
prime[++prime[0]] = i;
}
for (int j = 1; j <= prime[0]; ++j) {
if (i * prime[j] > MAXN) break;
prime[i * prime[j]] = 1;
if (i % prime[j] == 0) break;
}
}
return;
}
int main() {
init_prime();
int p, q, r, s;
while (cin >> p >> q >> r >> s) {
// 全局变量一定得在多输入内进行初始化
memset(arr, 0, sizeof(arr));
arr[0][0] = p, arr[0][1] = s, arr[0][2] = r - s;
arr[0][3] = r, arr[0][4] = q, arr[0][5] = p - q;
for (int len = 0; len < 6; ++len) {
int n = arr[0][len];
for (int i = 1; i <= prime[0] and prime[i] <= n; ++i) {
int cnt = 0, num = n;
while (num) {
cnt += num / prime[i];
num /= prime[i];
}
arr[prime[i]][len] = cnt;
}
}
for (int j = 2; j < MAXN; ++j) {
arr[j][0] = arr[j][0] + arr[j][1] + arr[j][2] - arr[j][3] - arr[j][4] - arr[j][5];
}
double ans = 1.0;
for (int j = 2; j < MAXN; ++j) ans *= pow(j, arr[j][0]);
printf("%.5lf\n", ans);
}
return 0;
}