(sf{Description})
(sf{Solution})
( ext{Subtask 1})
保证 (m=1)。
定义 (f[i][j]) 为前 (i) 棵竹子砍了 (j) 次剩下的最高竹子的最小值,就有一个 (mathcal O(nk^2)) 的 (mathtt{dp}):
( ext{Subtask 2})
最大值最小应该想到二分。
因为有竹子会砍没的情况,所以不好贪心。
正难则反(话说自己根本想不到好吗),倒着考虑:一开始竹子高度全为 (
m mid),每天竹子会长矮 (a_i),你可以选择 (k) 个拔高 (p),要求 过程中 不能 (< 0) 且最终所有竹子均 (geq h_i)(倒着来就是先长矮再拔高)。
为什么不能 (< 0)?这相当于从土地里面长出来,意味着高度被砍成负数。所以当竹子高度 (<0) 时,设此时刻 加一 的时刻为 (t),我们必须在 ([t,m]) 时刻区间内将竹子拔高。另外,从这里也可以看出越后面的拔高次数越珍贵,我们需要尽量拖延到 "非拔不可" 的时候拔高。所以每棵竹子的生长函数也是 唯一 的。
为什么保证最终所有竹子均 (geq h_i)?可以画一张图,将天数为 (x) 坐标,将高度为 (y) 坐标,把满足以上条件的 (i) 竹子的生长函数画出来(还要保证以 ((m, m mid)) 为终点)。如果生长函数的初始位置 ((0,y)) 的 (y<h_i),就需要将整个函数向上平移到 ((0,h_i)) 才是这棵竹子的生长轨迹,这样你会发现它最终比 ( m mid) 高了,这是不合法的。 所以,在计算出生长函数后,我们需要再拔高竹子使得 (yge h_i),这时使用的拔高次数就可以使用 (m) 天中的任何一天了。
你可能会疑惑。我又 tm 在这里搞了很久,考场上硬是打了一半没打了。
为什么计算出每棵竹子的生长函数后,可以用这个生长函数来生长这棵竹子呢?毕竟,保证了 (yge h_i),也就意味着将生长函数应用在这棵竹子上,竹子的生长轨迹是生长函数向下平移的图像。而题目中要求砍竹子时要和 (0) 取 (max),不会出问题吗?事实上,当把竹子砍成 (0) 时,我们就让它变成 (0),这肯定比同样时刻生长函数的 (y) 坐标更优了!
具体实现时,可以朴素地 (mathcal O(nmlog V)) 来计算。更好的解法是将 (n) 棵竹子的 (m) 天都放在一起算:因为总共砍伐次数只有 (mk),开 (m) 个队列,第 (i) 个队列记录第 (m-i+2) 天必须砍的竹子。这是 (mathcal O((n+mk)cdot log V)) 的。
(sf{Code})
#include <cstdio>
#define rep(i,_l,_r) for(register signed i=(_l),_end=(_r);i<=_end;++i)
#define fep(i,_l,_r) for(register signed i=(_l),_end=(_r);i>=_end;--i)
#define print(x,y) write(x),putchar(y)
typedef long long ll;
template <class T> inline T read(const T sample) {
T x=0; int f=1; char s;
while((s=getchar())>'9'||s<'0') if(s=='-') f=-1;
while(s>='0'&&s<='9') x=(x<<1)+(x<<3)+(s^48),s=getchar();
return x*f;
}
template <class T> inline void write(const T x) {
if(x<0) return (void) (putchar('-'),write(-x));
if(x>9) write(x/10);
putchar(x%10^48);
}
#include <queue>
#include <iostream>
using namespace std;
typedef pair <int,int> Pair;
const int maxn=1e5+5,maxm=5005;
int n,m,k,p,h[maxn],a[maxn];
ll ans;
queue <Pair> q[maxm];
bool ok(ll x) {
ll t; Pair u;
rep(i,1,m) while(!q[i].empty()) q[i].pop();
rep(i,1,n) {
t=x/a[i]+1;
if(t<=m) q[t].push(make_pair(i,0));
}
for(int i=1,Cut=0;i<=m;++i,Cut+=k) {
while(!q[i].empty()) {
if(Cut) --Cut;
else return 0;
u=q[i].front(); q[i].pop(); ++u.second;
t=(x+1ll*p*u.second)/a[u.first]+1;
if(t<=m) q[t].push(u);
}
}
t=0;
rep(i,1,n)
t+=(max(0ll,1ll*a[i]*m+h[i]-x)+p-1)/p;
return t<=1ll*m*k;
}
int main() {
n=read(9),m=read(9),k=read(9),p=read(9);
rep(i,1,n) h[i]=read(9),a[i]=read(9);
ll l=0,r=0,mid;
rep(i,1,n) r=max(r,1ll*a[i]*m+h[i]);
while(l<=r) {
mid=l+r>>1;
if(ok(mid)) ans=mid,r=mid-1;
else l=mid+1;
}
print(ans,'
');
return 0;
}