洛谷 P8749:[蓝桥杯 2021 省 B] 杨辉三角形 ← 组合数 + 二分

【题目来源】
https://www.luogu.com.cn/problem/P8749
https://www.acwing.com/problem/content/3421/

【题目描述】
下面的图形是著名的杨辉三角形:

如果我们按从上到下、从左到右的顺序把所有数排成一列,可以得到如下数列:
1,1,1,1,2,1,1,3,3,1,1,4,6,4,1,...
给定一个正整数 N,请你输出数列中第一次出现 N 是在第几个数。

【输入格式】
输入一个整数 N 。

【输出格式】
输出一个整数代表答案。

【输入样例】
6

【输出样例】
13

【数据范围】
对于 20% 的评测用例, 1≤N≤10;
对于所有评测用例, 1≤N≤10^9

【算法分析】
● 杨辉三角(又称帕斯卡三角)是一个由数字排列成的三角形数表,其核心规律是每行首尾均为 1,中间每个数等于它上方两个数之和,本质上是二项式系数的直观排列,是数学与信息学中递推思想的经典模型。

● 杨辉三角是二项式系数在三角形中的一种几何排列。
(1)杨辉三角第 n 行(n≥1)包含 n 个数字,对应二项式 (a+b)ⁿ⁻¹ 展开式的系数。其中,(a+b)ⁿ = C(n,0)aⁿ + C(n,1)aⁿ⁻¹b¹ + C(n,2)aⁿ⁻²b² + ... + C(n,n-1)a¹bⁿ⁻¹ + C(n,n)bⁿ。
(2)杨辉三角第 n 行第 k 个数等于组合数 C(n-1,k-1)。其中,C(n,k) = n! ∕ (k!·(n−k)!)。
(3)杨辉三角每行数字左右对称:C(n,k)=C(n,n−k)。
(4)杨辉三角第 n 行的数字和为 2ⁿ⁻¹。
(5)杨辉三角左对齐后,沿‌ 45° 斜线‌(↙方向)的数字和构成斐波那契数列。

(6)杨辉三角左对齐后,第 i 行第 j 列的数=第 i-1 行第 j-1 列的数+第 i-1 行第 j 列的数。正是组合数性质 C(i, j)=C(i-1, j-1)+C(i-1, j) 的几何体现。

● 计算给定数字在杨辉三角中按行优先遍历首次出现的序号(从 1 开始计数
(1)由于杨辉三角的每行数字均呈严格左右对称,且满足组合数性质 C(n,k)=C(n,n−k),因此在计算与构造时,我们仅需关注对称轴左侧的数字即可。
(2)若行号与列号均从 1 开始计数,则杨辉三角第 i 行对称轴上的数可表示为组合数 C(2(i−1), i−1)。显然,图示各行对称轴上的数分别为 C(0,0)=1、C(2,1)=2、C(4,2)=6、C(6,3)=20。
(3)杨辉三角对称轴左侧的数字,在每条左上至右下的斜线(↙)上单调不减,在每条左至右的横线(→)上也单调不减。因此,可以采用二分的思想进行求解。

● 核心代码解析
(1)求组合数 C(a,b)
下面代码基于 C(a,b)=a×(a-1)×...×(a-b+1)/(1×2×...×b) 编写。这是求组合数最稳、最常用的安全写法。

cpp 复制代码
LL C(int a,int b) {
    LL res=1;
    for(int i=a,j=1;j<=b;i--,j++) {
        res=res*i/j;
        if(res>n) return res;
    }
    return res;
}

(2)由于 C(32,16)≤10^9<C(34,17),所以依据"++若行号与列号均从 1 开始计数,则杨辉三角第 i 行对称轴上的数可表示为组合数 C(2(i−1), i−1)++"知,所以最多只需要查找 17 斜列 即可在题目范围 1e9 内找到对应数字。

【算法代码】

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;

typedef long long LL;
LL n;

LL C(int a,int b) {
    LL res=1;
    for(int i=a,j=1; j<=b; i--,j++) {
        res=res*i/j;
        if(res>n) return res;
    }
    return res;
}

bool check(int k) {
    LL le=k*2,ri=max(n,le);
    while(le<ri) {
        LL mid=le+ri>>1;
        if(C(mid,k)>=n) ri=mid;
        else le=mid+1;
    }
    if(C(ri,k)!=n) return false;
    cout<<ri*(ri+1)/2+k+1;
    return true;
}

int main() {
    cin>>n;
    int k=16;
    while(!check(k)) {
        k--;
    }

    return 0;
}

/*
in:6
out:13
*/

【参考文献】
https://www.acwing.com/solution/content/179551/
https://blog.csdn.net/hnjzsyjyj/article/details/147570319
https://www.acwing.com/solution/content/167373/
https://www.luogu.com.cn/problem/solution/P8749
https://www.acwing.com/video/2842/

相关推荐
Lauren_Blueblue3 小时前
第十六届蓝桥杯省赛Python研究生组-F串
python·算法·蓝桥杯·算法基础
仟濹3 小时前
【算法打卡day39(2026-04-06~08 周一~周三)】(10道蓝桥杯真题)今日练习:蓝桥杯第13届省赛B组Cpp组
算法·职场和发展·蓝桥杯
小年糕是糕手3 小时前
【35天从0开始备战蓝桥杯 -- Day9】
数据结构·数据库·c++·算法·蓝桥杯
List<String> error_P1 天前
蓝桥杯最后几天冲刺
蓝桥杯
Tanecious.1 天前
蓝桥杯备赛:Day8-小苯的异或和
c++·蓝桥杯
Lauren_Blueblue1 天前
第十六届蓝桥杯省赛Python研究生组-C变换数组
python·算法·蓝桥杯·编程基础
Tanecious.1 天前
蓝桥杯备赛:Day8-小红杀怪
c++·蓝桥杯
The_era_achievs_hero1 天前
电子签名(蓝桥杯)
前端·蓝桥杯
2301_800895101 天前
第七届蓝桥杯b组省赛--备战蓝桥杯版h
蓝桥杯