题目描述
下面的图形是著名的杨辉三角形:
如果我们按从上到下、从左到右的顺序把所有数排成一列,可以得到如下
数列:
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 ≤ 1000000000。
题意: 在杨辉三角中找到第一个出现n的位置。
分析: 多画几行杨辉三角找找规律:
由于左右对称,右边出现的数字不如左边更优,于是只考虑三角形左侧部分。然后斜着看这些数字,可以把这些数字分成若干组,每组内部递增,这启发我们用二分的方法去找n,于是从最右侧的条带上二分,之所以从这条开始是因为从这条上找到的n会其它位置都更优,这条条带二分完后再到左边一个上面二分,直至第一次找到n,在所有条带前1e9个数字内一定会出现n,于是二分的范围也就确定了。
具体代码如下:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <string>
#define int long long
using namespace std;
//只考虑左侧且斜着看
//第i斜条的起始元素为C(2*(i-1), i-1)
//每一斜条的第k元素就是C(2*(i-1)+k-1, i-1)
int n;
int c(int a, int b)//直接暴力求组合数
{
if(b > a-b) b = a-b;
int ans = 1;
for(int i = 1; i <= b; i++)
{
ans = ans*(a-i+1)/i;
if(ans > n)//防止越界
return n+1;
}
return ans;
}
signed main()
{
cin >> n;
if(n == 1)
cout << 1;
else
{
for(int i = 16; i >= 1; i--)
{
int l = 2*i, r = 1000000000, ans = -1;
while(l <= r)
{
int m = l+r>>1;
if(c(m, i) >= n)
{
ans = m;
r = m-1;
}
else
l = m+1;
}
if(ans != -1 && c(ans, i) == n)
{
cout << (1+ans)*ans/2+i+1;
break;
}
}
}
return 0;
}