题目
思路
首先最大值最小考虑二分,假设我们检验
x
x
x
但是发现检验比较难写
尝试从图像分析
那么画出来图像大致如下:
然后我们发现可以将图像上移末端重合至
(
m
,
x
)
(m,x)
(m,x) 点
那么只需要倒推改成每天减小 a i a_i ai 高度,每天有 k k k 次机会增加 p p p,保证初始高度大于 h i h_i hi 即可
然后首先可以想到一个类似堆的做法,每次要死了就加,没来得及加的机会存下来
最后调整高度
时间复杂度
O
(
m
k
l
o
g
n
l
o
g
A
)
O(mklog nlogA)
O(mklognlogA)
发现可以更简单在每个必死时间点存储,
倒推要死的时候增加
最后调整高度就可以不用堆
时间复杂度
O
(
m
k
l
o
g
A
)
O(mklogA)
O(mklogA)
代码
#include<set>
#include<map>
#include<cmath>
#include<queue>
#include<stack>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<iostream>
#include<algorithm>
using namespace std;
#define LL long long
int read(){
bool f=0;int x=0;char c=getchar();
while(c<'0'||'9'<c){if(c=='-')f=1;c=getchar();}
while('0'<=c&&c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
return !f?x:-x;
}
#define mp make_pair
const int MAXN=100000;
const int INF=0x3f3f3f3f;
typedef pair<int,int> pii;
LL n,m,k,p;
LL h[MAXN+5],a[MAXN+5];
queue<pii> q[MAXN+5];
bool check(LL x){
for(int i=1;i<=m;i++)
while(!q[i].empty())
q[i].pop();
for(int i=1;i<=n;i++){
LL s=x/a[i]+1;
if(s<=m) q[s].push(mp(i,0));
}
for(int i=1,c=0;i<=m;i++,c+=k){
while(!q[i].empty()){
if(!c) return 0;
c--;
pii u=q[i].front();
q[i].pop(),u.second++;
LL s=(u.second*p+x)/a[u.first]+1;
if(s<=m) q[s].push(u);
}
}
LL s=0;
for(int i=1;i<=n;i++)
s+=(max(h[i]+a[i]*m-x,0ll)+p-1)/p;
return s<=m*k;
}
int main(){
//freopen("bamboo.in","r",stdin);
//freopen("bamboo.out","w",stdout);
n=read(),m=read(),k=read(),p=read();
LL L=0,R=0;
for(int i=1;i<=n;i++)
h[i]=read(),a[i]=read(),R=max(R,h[i]+m*a[i]);
while(L+1<R){
LL Mid=(L+R)>>1;
if(check(Mid)) R=Mid;
else L=Mid;
}
printf("%lld\n",R);
return 0;
}