题解 CF505E 【Mr. Kitayuta vs. Bamboos】
AutumnKite · · 题解
宣传一波博客
题意
有
每天白天,你可以选择
最小化
题解
首先最小化最大的...这种问题,显然可以用二分答案。
二分
考虑怎么解决这个判定性问题。
如果按照题意一天一天模拟,就需要考虑把竹子高度减
所以我们尝试倒着模拟这一过程。
即:竹子初始高度都设为
此时你必须保证竹子减少
这样就好做了。我们用一个堆维护 当前状态下继续减少高度而不“拔高”,第
(不理解这句话可以尝试看代码理解)
每次取出最快
最后判断堆是否为空即可,因为堆中维护的是
时间复杂度
代码
Code
#include <cstdio>
#include <cctype>
#include <cstring>
#include <algorithm>
int read(){
register int x = 0;
register char f = 1, ch = getchar();
for (; !isdigit(ch); ch = getchar()) if (ch == '-') f = 0;
for (; isdigit(ch); ch = getchar()) x = (x << 1) + (x << 3) + (ch ^ '0');
return f ? x : -x;
}
int n, m, k, c[100005];
long long p, a[100005], h[100005], l = 0, r, mid, ans;
struct node{
int day, id; // 表示当前状态下(二分的高度+c[id]*p)day+1天后竹子id的高度会<0
bool operator < (const node &b) const { // 默认大根堆,所以重载<时写的是>
return day > b.day;
}
};
struct Heap{ // 用algorithm中的堆相关的算法封装实现。
node h[200005];
int sz;
void clear(){ sz = 0; }
bool empty(){ return !sz; }
void push(node x){ h[++sz] = x, std :: push_heap(h + 1, h + 1 + sz); }
node pop(){ return std :: pop_heap(h + 1, h + 1 + sz), h[sz--]; }
node top(){ return h[1]; }
}H;
bool check(long long x){
H.clear(), memset(c, 0, sizeof c); // c[i]表示竹子i被“拔高”了几次
for (register int i = 1; i <= n; ++i)
if (x - a[i] * m < h[i]) H.push((node){x / a[i], i}); // 初始堆的状态
for (register int i = 1; !H.empty() && i <= m; ++i) // i表示倒着的第几天
for (register int j = 1; !H.empty() && j <= k; ++j){ // 拔高k根竹子
node u = H.pop();
if (u.day < i) return 0; // 无论怎么“拔高”都不能满足条件
++c[u.id]; // “拔高”
if (x + c[u.id] * p - a[u.id] * m < h[u.id]) // 还是不满足条件,就插入堆中
H.push((node){(x + c[u.id] * p) / a[u.id], u.id});
}
return H.empty();
}
int main(){
n = read(), m = read(), k = read(), p = read();
for (register int i = 1; i <= n; ++i)
h[i] = read(), a[i] = read(), r = std :: max(r, h[i] + a[i] * m); // 二分上界
while (l <= r) check(mid = l + r >> 1) ? ans = mid, r = mid - 1 : l = mid + 1;
printf("%lld", ans);
}
蒟蒻代码,巨佬勿喷。有错误私信哦~