题意
n
个竹子长度为
题解
先二分答案
M
,反向思考,转化为:第一天所有竹子都高为
设竹子最后高度为
h′i
如果一直不去增高竹子最后一天有三种情况:
1.
hi<=h′i
2.
0≤h′i<hi
3.
h′i<0
将所有的竹子放到堆中,不断取出最快的长度变成负的一根竹子,把它增高,如果他最终还会比 hi 小,就把它放到堆中。判断中间不合法的情况,和最后堆是否为空来判断当前答案 M <script type="math/tex" id="MathJax-Element-16">M</script>是否法。
代码
/// by ztx
#include <cstdio>
#include <queue>
#include <cstring>
#define MA(a,b) (a<(b)?a=(b):0)
#define Rep(i,l,r) for(i=(l);i<=(r);i++)
#define maxn 100010LL
typedef long long ll;
struct Data {
int id, day;
bool operator < (const Data&B) const { return day > B.day; }
};
int n, m, k, cnt[maxn];
ll p, h[maxn], a[maxn];
std::priority_queue<Data>q;
inline bool ok(ll H) {
int i, j, id, day;
while (!q.empty()) q.pop();
memset(cnt,0,sizeof cnt);
Rep (i,1,n) if (H-a[i]*m < h[i]) q.push((Data){i,H/a[i]}) ;
for (i = 1; !q.empty() && i <= m; i ++ )
for (j = 1; !q.empty() && j <= k; j ++ ) {
id = q.top().id, day = q.top().day, q.pop();
if(day < i) return false;
if(cnt[id] ++ , H+p*cnt[id]-a[id]*m < h[id])
q.push((Data){id,(H+p*cnt[id])/a[id]});
}
return q.empty();
}
int main() {
int i;
ll L = 0, R = 0, M;
scanf("%d%d%d%lld", &n, &m, &k, &p);
Rep (i,1,n)
scanf("%lld%lld", &h[i], &a[i]),
MA(L,a[i]), MA(R,h[i]+a[i]*m);
L -- ;
while (R-L > 1) { // (L,R]
M = (L+R)/2LL;
ok(M) ? R = M : L = M;
}
printf("%lld\n", R);
return 0;
}