要在这堆数字中找到n
观察发现:
(1)三角形左右两边的数字对称
我们只需要看左半边的数字即可
(2)画一条中轴线在中间
发现中轴线上的点的值为C(2n,n)
找的时候,从内往外找,依次去枚举每一斜行。
为什么?
假设我们找到n
,那外面的数字必然是小于n
的,所以我们从内开始去找n。
第一次找到的数必定在左边且减少我们的枚举次数。
接下来怎么做?
我们从第16斜行去枚举中轴线上的起点开始,依次去枚举每一斜行
从中查找组合数,看能否二分出答案来。
组合数:C(a,b)
a:底数–>当前枚举的斜行数–>二分的答案(r
)
**b:**真数–>k
不清楚二分的过程可以看下面的图:
组合数:C(a,b)
a:底数–>当前枚举的斜行数–>二分的答案(r
)
b:真数–>k
枚举的是每一斜行,从中轴线的起点开始。
相当于我check
一个k
(枚举的斜行数),我二分的是当前这一斜行的组合数的底数。
根据二分原理,去更新mid
的值。
再看我当前枚举的这一斜行C(mid,k)
能否找到n
枚举这一斜行都找不到后,我再往前去枚举上一斜行,直至第一个斜行
public static long C(long a,long b) {
long res=1;
for(long i=a,j=1;j<=b;i--,j++) {
res=res*i/j;
if(res>n)return res;
}
return res;
}
在数字序列的第几个位置:(r+1)*r/2+k+1
r
:组合数的底数
import java.util.*;
public class Main{
static int n;
public static long C(long a,long b) {
//求组合数
long res=1;
for(long i=a,j=1;j<=b;i--,j++) {
res=res*i/j;
if(res>n)return res;
}
return res;
}
public static boolean check(int k) {
long l=2*k,r=Math.max(l,n);
//记得l,n取max,否则最一开始当l>r时无法二分
//C(l,r):l表示的是组合数底数的下界
// r表示的是组合数底数的上界
//二分
while(l<r) {
long mid=l+r>>1;
if(C(mid,k)>=n)r=mid;
//更新我当前枚举的这一斜行组合数的底数
//让它往上或往下走
else l=mid+1;
}
if(C(r,k)!=n)return false;
//找不到等于n的数,就返回false,去枚举下一斜行。
//C(r,k):二分出的组合数
//k:k表示的是列数,由于是从第0行开始,所以是k+1
//这样可以得出数字n在第几列。
//r:r表示的是行数,(1+r)*r/2可以得到数字在哪一行
//(1+r)*r/2再加上移动的第几列即k+1
//这样可以得到n在整个序列中是在第几个位置。
System.out.println((1+r)*r/2+k+1);
return true;
}
public static void main(String []args){
Scanner sc=new Scanner(System.in);
n=sc.nextInt();
for(int i=16;;i--){
//C16的32小于1e9
//从16斜行开始枚举
//找到满足条件的数就break
if(check(i))break;
}
}
}