二分最大值,然后考虑如何处理.
正着做很难,因为每次减掉 $p$ 后还要与 0 取 max,但是倒着做就会容易很多.
先将所有数的高度都置为 $mid$,那么就有两种操作:
1. -a[i]
2. +p
显然每次减掉 $a[i]$ 后不可以小于 0,因为如果减掉 $a[i]$ 后小于 0 的话意味着正着做到这一步没有与 0 取max.
那么减掉 $a[i]$ 后小于 0 的话就要加上 p.
我们贪心给最需要加上 p 的位置加上 p (即最快小于 0 的位置),然后如果 +p 操作不够的话就不合法.
如果合法,最终每个数的大小都要大于等于 $h_{i}$.
整个贪心过程用堆维护即可(实际上我们在堆中维护的是最小小于 $h_{i}$ 的时刻,不过反正都一样)
code:
#include <cstdio> #include <cstring> #include <queue> #define N 100009 #define ll long long #define setIO(s) freopen(s".in","r",stdin) using namespace std; int n,m,K,P; int h[N],a[N],c[N]; struct node { int id,day; node(int id=0,int day=0):id(id),day(day){} bool operator<(const node b) const { return day>b.day; } }; priority_queue<node>q; int check(ll val) { memset(c,0,sizeof(c)); while(!q.empty()) q.pop(); for(int i=1;i<=n;++i) { if(val-1ll*a[i]*m<h[i]) { q.push(node(i,val/a[i])); } } for(int i=1;i<=m&&!q.empty();++i) { for(int j=1;j<=K&&!q.empty();++j) { node e=q.top(); q.pop(); if(e.day<i) return 0; ++c[e.id]; if(val-1ll*a[e.id]*m+1ll*c[e.id]*P<h[e.id]) { q.push(node(e.id,(val+1ll*c[e.id]*P)/a[e.id])); } } } return q.empty(); } int main() { // setIO("input"); scanf("%d%d%d%d",&n,&m,&K,&P); ll l=1,r=0,mid,ans=0; for(int i=1;i<=n;++i) { scanf("%d%d",&h[i],&a[i]); r=max(r,1ll*m*a[i]+h[i]); } while(l<=r) { mid=(l+r)>>1; if(check(mid)) ans=mid,r=mid-1; else l=mid+1; } printf("%lld ",ans); return 0; }