蓝桥杯历届真题 杨辉三角形【第十二届】【省赛】【B组】
题目大意
将杨辉三角形按照从上到下、从左至右的顺序排成一个数列。输入一个正整数
N
N
N,请你判断该数列中第一次出现
N
N
N是数列中第几个数。
解题思路
-
思路一
找 规 律 找规律 找规律
1 1 1
1 1 1 1 1 1
1 1 1 2 2 2 1 1 1
1 1 1 3 3 3 3 3 3 1 1 1
1 1 1 4 4 4 6 6 6 4 4 4 1 1 1
1 1 1 5 5 5 10 10 10 10 10 10 5 5 5 1 1 1
-
观察上面杨辉三角形,可以发现第一列全是 1 1 1,第二列为从 0 0 0开始每次递增 1 1 1的递增序列,第三列为序列 1 、 2 、 3 … n 1、2、3…n 1、2、3…n(即第二列)的前缀和序列。
-
由于 N N N的最大是 1 e 9 1e9 1e9,第一列全为 1 1 1,不用考虑,第二列每次递增 1 1 1,若要达到最大值,定义数组的内存肯定超限。然后第三列通过计算 n ∗ ( n + 1 ) / 2 > 1 e 9 n*(n+1)/2>1e9 n∗(n+1)/2>1e9可以得到 n > 44721 n>44721 n>44721,由于第三列第一行和第二行都为 0 0 0,所以最小需要44723行。
-
当第三列的值已经大于 1 e 9 1e9 1e9时,不需要再计算后面的数,直接根据第二列规律,找第二列中 n n n的位置即可。由于第二列是从 0 0 0开始的,此时可以确定 n n n是在第 n + 1 n+1 n+1行,又因为是第二列,所以 n n n的是数列中第 n ∗ ( n + 1 ) / 2 + 2 n*(n+1)/2+2 n∗(n+1)/2+2个。
附上代码
#pragma GCC optimize("-Ofast","-funroll-all-loops") #include<bits/stdc++.h> #define int long long #define lowbit(x) (x &(-x)) #define endl '\n' #define debug(x) cout<<"# "<<x<<endl; using namespace std; const int INF=0x3f3f3f3f3f3f3f3f; const int dir[4][2]={-1,0,1,0,0,-1,0,1}; const double PI=acos(-1.0); const double e=exp(1.0); const double eps=1e-10; const int M=1e9+7; const int N=2e5+10; typedef long long ll; typedef pair<int,int> PII; typedef unsigned long long ull; int a[N]; signed main(){ ios::sync_with_stdio(false); cin.tie(0);cout.tie(0); int n,k=1; cin>>n; a[0]=1; if(n==1) cout<<1<<endl; else{ for(int i=1;i<44725;i++){ for(int j=i;j>=1;j--){ a[j]+=a[j-1]; if(a[j]==n){ cout<<k+i-j+1<<endl; return 0; } } k+=(i+1); } cout<<(1+n)*n/2+2<<endl; } return 0; }
- 思路二
二 分 + 组 合 数 二分+组合数 二分+组合数
-
根据杨辉三角形与组合数的联系,第 i i i行第 j j j列的数都是组合数 C ( i , j ) C(i, j) C(i,j),因为 C ( n , 1 ) = n C(n, 1) = n C(n,1)=n,所以对于每个输入的 n n n必定有解。
-
由于杨辉三角形是左右对称的,且数列是按照每行从左到右排列的,所以数字第一次出现必定在左边。
1 1 1
1 1 1
1 1 1 2
1 1 1 3
1 1 1 4 6
1 1 1 5 10
1 1 1 6 15 20 -
根据题意 n n n最大 1 e 9 1e9 1e9,且 C ( 34 , 17 ) > 1 e 9 C(34, 17) > 1e9 C(34,17)>1e9, C ( 32 , 16 ) < 1 e 9 C(32, 16) < 1e9 C(32,16)<1e9,因此只要枚举前 16 16 16个斜行即可
-
观察上面三角形可以看出,除了第一条边,一条边上的数都是严格单调递增的,所以我们可以对每一条边进行一次二分操作
-
组合数计算需要用数学公式 C ( n , m ) = n ! ( n − m ) ! ∗ m ! = ( n − m + 1 ) ( n − m + 2 ) ∗ … ∗ n 1 ∗ 2 ∗ … ∗ m C(n,m)=\frac{n!}{(n-m)!*m!}=\frac{(n-m+1)(n-m+2)*…*n}{1*2*…*m} C(n,m)=(n−m)!∗m!n!=1∗2∗…∗m(n−m+1)(n−m+2)∗…∗n直接算,如果数组的话会超内存。
附上代码#pragma GCC optimize("-Ofast","-funroll-all-loops") #include<bits/stdc++.h> #define int long long #define lowbit(x) (x &(-x)) #define endl '\n' #define debug(x) cout<<"# "<<x<<endl; using namespace std; const int INF=0x3f3f3f3f3f3f3f3f; const int dir[4][2]={-1,0,1,0,0,-1,0,1}; const double PI=acos(-1.0); const double e=exp(1.0); const double eps=1e-10; const int M=1e9+7; const int N=2e3+10; typedef long long ll; typedef pair<int,int> PII; typedef unsigned long long ull; int C(int a,int b,int n){ int ans=1; for(int i=a,j=1;j<=b;i--,j++){ ans=ans*i/j; if(ans>n) return ans; } return ans; } bool check(int k,int n){ int l=2*k,r=max(n,l); while(l<r){ int mid=(l+r)/2; if(C(mid,k,n)>=n) r=mid; else l=mid+1; } if(C(r,k,n)!=n) return false; cout<<(r+1)*r/2+k+1<<endl; return true; } signed main(){ ios::sync_with_stdio(false); cin.tie(0);cout.tie(0); int n; cin>>n; for(int i=16;i>=0;i--){ if(check(i,n)) break; } return 0; }