Codeforces837F-Prefix Sums-二分加组合数(爆longlong了)

(有任何问题欢迎留言或私聊 && 欢迎交流讨论哦

题意:传送门

 原题目描述在最下面。
 对于一段序列ai,每次操作后ai'等于前缀和ai
 问多少次操作后序列最大值大于aim。

 之前在牛客上做了一道类似的简单题,牛客的题是输出k次变化后的矩阵。牛客简单在答案输出mod。本题不能取模,然后爆longlong了???不想用大整数,懒得敲java。没想到居然用long double水过了?

思路:

 很明显具有单调性。考虑二分操作次数。
 在纸上列举长度为4的数组,4次操作后每项的系数可以发现规律。
 第m次操作后:k = m - 1, 系数为:C(k, k), C(k+1, k), C(k+2, k) … C(k+n-1, k)
 坑点就在爆long long。用long double, 然后暴力水组合数就行了。
 具体看代码。

AC代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int INF = 1e9;
const int N = 1000007;
LL n, aim, ar[N];
bool ok(LL m){
  long double tmp = aim;
  LL k = m - 1;
  for(int i = 1; i <= n; ++i){
    long double a = 1, b = k + i - 1, res = ar[n-i+1];
    if(res==0)continue;
    LL x = min(k, i - 1LL);
    for(LL i = 1; i <= x; ++i){
      res = res*b/a;//这里就是水组合数C(k+i-1, k)
      b--;a++;
      if(res>=tmp) return 1;//注意res肯定是单增的,所以可以提前特判
    }
    tmp -= res;
    if(tmp<=0)return 1;
  }
  return 0;
}
int main(int argc, char const *argv[]){
  while(~scanf("%lld%lld", &n, &aim)){
    int flag = 0;
    for(int i = 1; i <= n; ++i){
      scanf("%lld", &ar[i]);
      if(ar[i] >= aim) flag = 1;
    }
    if(flag){
      printf("0\n");
      continue;
    }
    LL l = 1, r = (LL)1e18 + 5, mid, ans = 1;
    while(l <= r){
      mid = (l + r)>>1;
      if(ok(mid))r = mid - 1, ans = mid;
      else l = mid + 1, ans = mid + 1;
    }
    printf("%lld\n", ans);
  }
  return 0;
}


原题目描述:

这里写图片描述

猜你喜欢

转载自blog.csdn.net/qq_39599067/article/details/81143623