蓝桥杯ACwing系统

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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值