[蓝桥杯 2021 省 B] 杨辉三角形 详细过程+注释

原题链接:P8749 [蓝桥杯 2021 省 B] 杨辉三角形

题目描述

下面的图形是著名的杨辉三角形:

如果我们按从上到下、从左到右的顺序把所有数排成一列,可以得到如下数列:

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≤1e9 。

蓝桥杯 2021 第一轮省赛 B 组 H 题。


解题思路:

首先由于杨辉三角形是对称的,所以只需要在三角形的左半边查找,再根据杨辉三角形的性质,第i行第j列的值为C(i,j),可以得到下面部分(红色为有效的查找区间)

首先由于数据范围是10的9次方,所以暴力肯定不行

由于每一列的元素具有单调性,考虑使用二分

思考一下应该从哪一列开始查找,显然如果两个元素是相同的,那么显然行数更小的那个数最先出现

如图,C(4,2)显然比C(6,1)先出现

查找顺序:如果每一列都进行二分查找,那么从哪一列开始找才能最先找到最优解呢?

假设我在C(x,4)的第8行第4列C(8,4)找到了70这个数,那么我再到前面找,假设我在第2列又找到了70,也就是C(x,2)=70,那么这个x一定会大于8,那么C(x,2)的这个元素出现的顺序一定在C(8,4)之后(因为行数小的最先出现)

所以我们按列数从大到小,依次二分查找每一列元素,直到第一次找到这个元素为止

由图中可知,每一列第一个元素是C(i,i/2),那么根据数据范围,通过敲计算器或者敲一个C(i,i/2)可知:C(34,17)=2333606220,超过了1e9,所以只需要C(32,16)开始往下枚举每一列即可

代码实现💻

#include<bits/stdc++.h>
using namespace std;
#define int long long
int n;
int C(int a,int b)//求组合数
{
	int res=1;
	for(int i=1,j=a;i<=b;i++,j--)
	{
		res*=j;res/=i;
		if(res>n)return res;
//		组合数可能出现c(100000,16)然后数据过大得到负数的情况
//	    所以遇到大于n的答案就可以直接退出了
	}
	return res;
}
bool check(int mid,int ii)
{
	return C(mid,ii)<=n;//二分的左半边储存<=n的数
}
signed main()
{
//	cout<<C(34,17)<<endl;//算一下数据范围
	int res;
	cin>>n;
	if(n==1)cout<<1<<endl;
	else 
	{
		for(int i=16;i>=1;i--)
		{
			int j=i*2;
			int l=j-1,r=n+1;
			while(l+1<r)//用二分得到的l判断是否找到
			{
				int mid=(l+r)>>1;
				if(check(mid,i))l=mid;
				else r=mid;
			}
			if(C(l,i)==n)
			{
				res=(1+l)*l/2+(i+1);//根据行号,列号计算第几个出现
				break;
			}
		}
		cout<<res<<endl;
	}
	return 0;
}
  • 16
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值