正题
题目链接:https://www.luogu.com.cn/problem/CF505E
题目大意
开始一个长度为(n)的序列(h),(m)天每天你可以(k)次选择一个(h_i)让它等于(h_i=max{h_i-p,0}),然后结束时让每个(h_i=h_i+a_i),要求使得最后最大的(h)值最小。
(1leq nleq 10^5,1leq mleq 5 imes 10^3,1leq kleq 10)
解题思路
这个好像是以前WC还是APIO讲课的题了。
首先最大值最小直接上二分,然后考虑贪心做。
不过正着做不太好做,考虑倒着。首先因为每次减法减去的值都是一样的,所以如果有一株的高度大于(p)那么我们显然没有必要剪高度小于(p)的,因为这样会浪费。
设答案为(H),那么开始我们就让所有草的高度都是(H),然后每次所有的草高度减少(a_i),然后你可以(k)次拔高一棵草的高度(k)。要求全程没有草的高度小于零且最后第(i)棵草的高度都不小于(h_i)。
那么考虑如果一棵草不用再管高度都不会小于(h_i)那么显然不需要管这棵了,否则剪它肯定不会,为了满足条件我们肯定是减去最快小于(0)的那棵草。
时间复杂度:(O(log L(nlog n+mklog n)))
(code)
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#define ll long long
#define mp(x,y) make_pair(x,y)
using namespace std;
const ll N=1e5+10;
ll n,m,k,p,h[N],a[N],w[N];
priority_queue<pair<ll,ll> > q;
bool check(ll H){
while(!q.empty())q.pop();
for(ll i=1;i<=n;i++)
if(H-a[i]*m<h[i])w[i]=H,q.push(mp(-H/a[i],i));
for(ll i=1;i<=m;i++)
for(ll j=1;j<=k;j++){
if(q.empty())return 1;
ll z=-q.top().first;
ll x=q.top().second;
if(z-i<0)return 0;
q.pop();w[x]+=p;
if(w[x]-a[x]*m<h[x])
q.push(mp(-w[x]/a[x],x));
}
if(q.empty())return 1;
return 0;
}
signed main()
{
scanf("%lld%lld%lld%lld",&n,&m,&k,&p);
for(ll i=1;i<=n;i++)scanf("%lld%lld",&h[i],&a[i]);
ll l=0,r=1e9*(m+10);
while(l<=r){
ll mid=(l+r)>>1;
if(check(mid))r=mid-1;
else l=mid+1;
}
printf("%lld
",l);
return 0;
}