题解:P12876 [蓝桥杯 2025 国 Python A] 杨辉三角

· · 题解

题目传送门:这里

0. 前置知识

你只需要知道组合数公式 \binom{n}{m} =\frac{n!}{m!(n-m)!} 就可以了,非常简单。

学会了?让我们来看一看这道题吧。

1. 思路分析

观察数据范围, n \le 10 ^ 5,乍一看好像暴力无法通过。

然而事实真的如此吗?

至于怎么求这个最小行数?不会?没关系,我也不会求,但是我们可以 break,当我们枚举到的数大于 $n$ 时直接跳出不就行了嘛! 当然,这个思路还可以优化。如果你的注意力惊人,你就会发现: **杨辉三角形是对称的!** 所以我们只用枚举到对称轴就可以了。 ### 2. 实现 好了,思路想好了,可以开始写代码啦! 但是,直觉告诉我们这题不可能这么快结束。不出所料,我们还有一个关键问题没有解决: **如何求出当前枚举到的项?** 这就要用到我们的前置知识了。 我们现在枚举到了 $\binom{i}{j}$ ,我们希望的是从 $\binom{i}{j-1}$ 中求出当前项,有没有一个递推公式呢? 有的,兄弟,有的。接下来我们来推导一下: 我们知道: $$ \binom{i}{j} =\frac{i!}{j!(i-j)!}$$ $$ \binom{i}{j-1} =\frac{i!}{(j-1)!(i-j+1)!}$$ 我们将两式相除可以得到: $$\frac{\binom{i}{j}}{\binom{i}{j-1}} =\frac{\frac{i!}{j!(i-j)!}}{\frac{i!}{(j-1)!(i-j+1)!}}$$ 把 $i!$ 约掉: $$\frac{\binom{i}{j}}{\binom{i}{j-1}}=\frac{(j-1)!(i-j+1)!}{j!(i-j)!}$$ 回忆一下阶乘的定义,$n!$ 就是从 $1$ 乘到 $n$,所以原式中 $(j-1)!$ 和 $j!$ 可以约掉,剩一个 $j$,后面的同理。于是我们就可以得出: $$\frac{\binom{i}{j}}{\binom{i}{j-1}}=\frac{i-j+1}{j}$$ 我们要找的是 $\binom{i}{j}$,于是我们十字交叉相乘可得: $$\binom{i}{j}\times j=\binom{i}{j-1}\times (i-j+1)$$ 移项,终于得到了我们梦寐以求的公式: $$\binom{i}{j}=\binom{i}{j-1}\times (i-j+1)/j$$ 到这一步,问题就解决的差不多了。 ### 3. AC代码 ```cpp #include <bits/stdc++.h> using namespace std; #define int long long int n; int rma; int ans[100005];//每个数的出现次数 int sum[100005];//答案序列 int ma;//出现次数的最大值 signed main() { cin>>n; for(int i=2;i<=n;i++){ int c=1; for(int j=1;j<=i/2;j++){ c=c*(i-j+1)/j;//推导出来的公式,递推求组合数 if(c>n) break; if(j==i-j){//判断是否是对称轴上的数 //由于我们是按照组合数定义的,所以行号从0开始,第偶数行才有对称轴 ans[c]++; }else{ ans[c]+=2; } } } for(int i=2;i<=n;i++){ sum[ans[i]]++; ma=max(ma,ans[i]);//方便等会找输出的上界 } for(int i=1;i<=ma;i++){ if(sum[i]) cout<<i<<' '<<sum[i]<<endl; } return 0; } ``` ### 4. 小细节 有些童鞋(比如我)可能会这么想:杨辉三角的第一列永远是 $1$,那我为什么不直接从 $j=2$ 开始枚举呢? 于是喜提 $0$ 分... 我们的递推公式是从 $1$ 开始推导的,如果 $j$ 从 $2$ 开始枚举,就会导致组合数计算错误,当然无法通过,所以还是老老实实从 $1$ 开始吧。 那为什么 $i$ 可以从 $2$ 开始枚举呢?因为我们每一层的组合数都是从 $1$ 开始的,所以起始的行数并不重要,由于第 $0$ 行和第 $1$ 行都没有大于 $1$ 的数字,所以可以直接从 $i=2$ 开始枚举。