具体分析可以看:
第十二届蓝桥杯B组省赛杨辉三角形题解_Nervous_46216553的博客-CSDN博客
输入输出样例
示例 1
输入
6
输出
13
评测用例规模与约定
对于 20%的评测用例,1≤N≤10; 对于所有评测用例,1≤N≤1000000000。
运行限制
- 最大运行时间:1s
- 最大运行内存: 256M
第一眼的思路是打表算呢,打算从组合数一个一个算来着这样的时间复杂度挺高的。
第二种思路是找路径:用C(m,n)的组合数来看,当前位置比N大则向左走,小则向下走一次,向中间走一次:
#include<iostream>
using namespace std;
long long m;//行-1
long long n;//列-1
long long v;//C(m,n)的值
long long temp;//临时变量用来提速
void down(){//下一层
v=v*(m+1)/(m-n+1);
++m;
}
void left(){//左一个
v=v*n/(m-n+1);
--n;
}
void center(){//尝试往中间靠
temp=v*(m-n)/(n+1);//如果右边更大就向右,否则不用动
if(v<temp){
v=temp;
++n;
}
}
int main(){
long long N,ans=0;
cin >> N;
v=1;
m=1;
n=0;
while(1){//查找
if(v<N){
down();
center();
}else if(v>N){
left();
}else{
break;
}
}
//检查位置正确性
//cout << m+1 << " " << n+1 << endl;
ans=m*(m+1)/2+n+1;//前面的等差数列求和然后加当前行
if(ans==2)ans=1;
cout << ans << endl;
return 0;
}
第三种思路是二分:
利用列的递增性质我们进行二分:
#include<iostream>
using namespace std;
long long N;
long long P(long long m,long long n){//计算m行n列的值
--m;//P(m,m)=C(m-1,n-1)
--n;
n=max(n,m-n);//C(m,n)==C(m,m-n)
long long ans=1;
for(long long i=1;i<=(m-n);++i){
ans*=(n+i);
ans/=i;//由上文定理一定能除尽
if(ans>N){//及时退出防止溢出(只用大小关系,不需要准确值)
return N+1;
}
}
return ans;
}
long long find(long long n){//二分查找第n列
long long gap=1<<30,m=n;//不存在m不小于n的位置!!!
while(gap){
if(P(m,n)<N){//小了往前跑
m+=gap;
}else if(P(m,n)>N){//大了间距缩小往前找
gap/=2;//(8,4,2,1一定能凑出<=15的所有数(就是二进制表示吗))
m-=gap;
}else{
return (m)*(m-1)/2+n;//前面的行等差数列求和然后加当前行
}
}
return (N+1)*(N+1);//找不到就返会一个一定超了的位置,别的如果找到一定排的靠前。
}
int main(){
long long ans=1;
cin >> N;
if(N!=1)//因为第一列全是1,值相等所以不可以用二分查找
ans=find(2);
for(int i=3;i<=100;++i){//第N行总和2^n(每行的总和都是上一行的两倍),最大值肯定>(2^n)/n,左侧100列以内绝对足够搜出10^9的数值(因为前文组合数计算优化过了,根本不怕溢出,查几列随便二分查找的速度很快很快)
ans=min(ans,find(i));//找最小的
}
cout << ans << endl;
return 0;
}