Description

疫情期间,牛牛宅在家里无事可做,于是就在网上买了n本书,每本书都有一个知识值为ai。每读一本书,牛牛的知识力就会上升ai点。当然了,因为牛牛的精力也是有限的,如果同一天连续读k本书,获得的知识力只能增加ai-k+1点。比如第一天看了知识值为5的书,那么牛牛会获得5点知识力,如果这一天在继续看另一本知识值为5的书,只能获得4点知识力,如果看了前面两本书后在继续看一本知识值为2的书,就只能获得0点知识力。牛牛想知道如果他要获得m点知识力,最少需要看几天。

注意:看书不需要按顺序,一本书只能看一次,书可以不看完,只要看就会增加知识力,当书增加的知识力为负时候可以选择不看,可以认为看完一本书是一瞬间的事情,看完后书就会消失。

Solution

Codeforces Round #540 (Div. 3) 原题
二分思路并不难想,考虑对贡献值从大到小排序
每次选取最大贡献的书,然后check的时候每走完x天,维护一个tot
简单的讲,就是第一轮先把每一天都用贡献最大的填上,然后第二轮填的时候每一本书的贡献就都要减1,这样不断填充就行了。

Code

#include<bits/stdc++.h>
typedef long long ll;
const int N = 1e6 + 5;
const int mod = 1e4 + 7;
using namespace std;
ll a[N], c[N];
int n, m;
bool check(int x) {
  ll res = 0, tot = 0, now = 0;
  for(int i = 1; i <= n; i++) {
    now++; res += max(0LL, a[i] - tot);
    if(now == x) {
      tot++, now = 0;
    }
  }
  return res >= m;
}
int main() {
  ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
  cin >> n >> m;
  for(int i = 1; i <= n; i++) cin >> a[i];
  sort(a + 1, a + 1 + n, greater<int>());
  int left = 1, right = 1e9;
  int ans = -1;
  while(left <= right) {
    int mid = left + right >> 1;
    if(check(mid)) {
      right = mid - 1;
      ans = mid;
    } else {
      left = mid + 1;
    }
  }
  cout << ans << "\n";
  return 0;
}