二分答案由最优问题变为可行性问题
首先可以发现,因为一个竹子在一天可以被砍多次,那么其实在不浪费的情况下一个竹子越往后被砍肯不是不会更劣的(因为如果砍成小于0了就会浪费)
然而我们并好不知道一个竹子应该被砍的准确时间,所以我们把倒着看这个问题,从最后一天开始 -- 所有的竹子的高度均为mid,每天竹子会减小a[i],你可以增加k个p给一些竹子,最后能否让所有竹子均大于等于他们的初始高度h[i]
这样一来,我们直接给最需要被使用技能竹子使用技能即可,因为这一定是最晚的时候时间,也是最优的
对于一些有下界没有上界的贪心问题,不妨试试倒着来看
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define gi get_int()
const int MAXN = 2e6;
#define int long long
int get_int()
{
int x = 0, y = 1;
char ch = getchar();
while (!isdigit(ch) && ch != '-')
ch = getchar();
if (ch == '-')
y = -1, ch = getchar();
while (isdigit(ch))
x = x * 10 + ch - '0', ch = getchar();
return x * y;
}
int plus[MAXN], m, n, k, p, h[MAXN], a[MAXN];
class Heap
{
public:
int height, index;
} heap[MAXN];
int hNum;
int operator< (const Heap& a1, const Heap& b)
{
int valueA = (a1.height + plus[a1.index] * p) / a[a1.index];
int valueB = (b.height + plus[b.index] * p) / a[b.index];
return valueA > valueB;
}
int check(int mid)
{
memset(plus, 0, sizeof(plus));
hNum = 0;
for (int i = 0; i < n; i++) {
if ((mid - h[i]) / a[i] < m) {
heap[hNum].height = mid;
heap[hNum].index = i;
hNum++;
}
}
if (hNum == 0) return 1;
std::make_heap(heap, heap + hNum);
for (int i = 0; i < m; i++) {
for (int j = 0; j < k; j++) {
int index = heap[0].index, height = heap[0].height;
std::pop_heap(heap, heap + hNum);
hNum--;
if (((height + plus[index] * p) / a[index]) < i + 1) return false;
plus[index]++;
if (height + plus[index] * p - a[index] * m < h[index]) {
std::push_heap(heap, heap + hNum);
hNum++;
}
if (hNum == 0)
return true;
}
}
return hNum == 0;
}
signed main()
{
n = gi, m = gi, k = gi, p = gi;
for (int i = 0; i < n; i++) {
h[i] = gi;
a[i] = gi;
}
int l = 0, r = 1e16, ans = -1;
while (l <= r) {
int mid = (l + r) / 2;
if (check(mid) == true) {
r = mid - 1;
ans = mid;
} else {
l = mid + 1;
}
}
std::cout << ans;
return 0;
}