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