https://www.acwing.com/problem/content/description/3421/
代码
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
int n;
int ans;
LL C(int a , int b)
{
// cab = a! / b! / (a - b)! = a * .... (a - b + 1) / b!
LL res = 1;
for (int i = a , j = 1 ; j <= b ; j ++ , i --)
{
res = res * i / j;
if(res > n) return res;
}
return res;
}
// 1
// 1
// 1 2
// 1 3
// 1 4 6
// 1 5 10
// 我们通过观察可以得到每一横行从左到右都是从大到小排的 , 每一斜行都是从右上角到左下角从小到大排的
// 我们可以知道要求 n cn1 = n 所以我们可以知道 n 一定是可以求出来的
// 那么我们知道每一个横行的最右端都等于 c(2n, n) 比如说 c21 = 2 , c42 = 6
// c(2n , n) 最大不超过 1e9 那么 n 最多可以取到 17 , 所以我们只需要从 17 到 1 枚举每一个斜行就可以了
// 因为是有序的 所以我们可以二分查找答案 , l = k * 2 , 因为我们知道的是每一横行的最中间一个数怎么求 c(2n , n)
// 我们要求的是 c(r , k) 我们枚举的是每一个斜行 我们找到了 c(r , k) == n 的时候我们就找到了答案
// c(r , k) 是答案 , 在第 r 行 第 k 列 所以答案应该是 1 + 2 + 3 + .. (r) + k + 1;
// 所以最终的答案就是 r * (r + 1) / 2 + k + 1;
// 时间复杂度是 17 * log(n) + 求组合数的
bool check(int k)
{
int l = k * 2 , r = max(l , n);
// l 表示的是当前这一个斜行的左上角 c(2n , n)
// 这个数最多是在 c(n , 1)处 , 所以在二分的时右边界一定是 <= n 的
// r 一定不能小于 l , 所以要和 l 取max 特别当 n = 1 时 , l = 2 r = 1 的话会出错
while(l < r)
{
int mid = l + r >> 1;
if(C(mid , k) >= n) r = mid; // 找到大于等于 n 的第一个数 , 由于是第一个 , 所以第一次找到的一定是答案
else l = mid + 1;
}
if(C(r , k) != n) return false;
// 13 的话就是 4 * 5 / 2 = 10 + 3 所以 r = 4
cout << r * (r + 1ll) / 2 + k + 1;
return true;
}
int main()
{
cin >> n;
for(int i = 17 ; ; i --)
if(check(i))
break;
return 0;
}