Codeforces 505E - Mr. Kitayuta vs. Bamboos(二分+堆)
首先很显然的一点是,看到类似于“最大值最小”的字眼就考虑二分答案 \(x\)(这点我倒是想到了)
然鹅之后就不会做了/wq/wq/wq
注意到此题正着处理不太方便,故考虑倒着处理,那么原题相当于,初始 \(b_i=x\),每次操作有以下步骤:
- \(\forall i,b_i\leftarrow b_i-a_i\) 并且要求修改过后的 \(b_i\geq 0\)
- 选择 \(k\) 个 \(b_i\) 并将它们加上 \(p\)
- 要求最后 \(\forall i,b_i\geq h_i\)
我们考虑建一个堆,堆里面维护所有元素的 \(\lfloor\dfrac{b_i}{a_i}\rfloor\) 的值,也就是每个元素最多减多少个 \(b_i\) 就会变到 \(0\)。然后我们每次贪心地选择 \(\lfloor\dfrac{b_i}{a_i}\rfloor\) 最小的元素并将其加上 \(p\),如果选完了还是发现有元素减去 \(a_i\) 后小于 \(0\) 就直接返回不合法即可。
最后考虑怎么判最终 \(b_i\geq h_i\) 是否成立。其实我们只需加一个小小的优化即可,不难发现对于某个 \(b_i\),如果我们给其加了 \(c_i\) 次 \(p\),并且满足 \(h_i+ma_i\leq x+c_ip\),那么我们就不用管这个元素了,因为不论怎样它最终都是大于 \(0\) 的,也就是说我们每次将某个 \(b_i\) 加上 \(p\) 之后如果发现 \(h_i+ma_i\leq x+c_ip\) 成立,那么我们就不用再将该元素压入堆了,最后检验堆是否为空即可。
时间复杂度 \((n+mk)\log n\log a_i\)。
#include <bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define fill0(a) memset(a,0,sizeof(a))
#define fill1(a) memset(a,-1,sizeof(a))
#define fillbig(a) memset(a,63,sizeof(a))
#define pb push_back
#define ppb pop_back
#define mp make_pair
template<typename T1,typename T2> void chkmin(T1 &x,T2 y){if(x>y) x=y;}
template<typename T1,typename T2> void chkmax(T1 &x,T2 y){if(x<y) x=y;}
typedef pair<int,int> pii;
typedef long long ll;
namespace fastio{
#define FILE_SIZE 1<<23
char rbuf[FILE_SIZE],*p1=rbuf,*p2=rbuf,wbuf[FILE_SIZE],*p3=wbuf;
inline char getc(){return p1==p2&&(p2=(p1=rbuf)+fread(rbuf,1,FILE_SIZE,stdin),p1==p2)?-1:*p1++;}
inline void putc(char x){(*p3++=x);}
template<typename T> void read(T &x){
x=0;char c=getchar();T neg=0;
while(!isdigit(c)) neg|=!(c^'-'),c=getchar();
while(isdigit(c)) x=(x<<3)+(x<<1)+(c^48),c=getchar();
if(neg) x=(~x)+1;
}
template<typename T> void recursive_print(T x){if(!x) return;recursive_print(x/10);putc(x%10^48);}
template<typename T> void print(T x){if(!x) putc('0');if(x<0) putc('-'),x=~x+1;recursive_print(x);}
void print_final(){fwrite(wbuf,1,p3-wbuf,stdout);}
}
const int MAXN=1e5;
int n,m,k,p,h[MAXN+5],a[MAXN+5];
int cnt[MAXN+5];
bool check(ll x){
memset(cnt,0,sizeof(cnt));
priority_queue<pair<ll,int>,vector<pair<ll,int> >,greater<pair<ll,int> > > q;
for(int i=1;i<=n;i++) if(h[i]+1ll*a[i]*m>x) q.push(mp(x/a[i],i));
if(!q.empty()&&q.top().fi==0) return 0;
for(int i=1;i<=m&&!q.empty();i++){
for(int j=1;j<=k&&!q.empty();j++){
pair<ll,int> pp=q.top();q.pop();
int id=pp.se;cnt[id]++;
if(h[id]+1ll*a[id]*m>x+1ll*p*cnt[id])
q.push(mp((x+1ll*p*cnt[id])/a[id],id));
}
if(!q.empty()&&q.top().fi<=i) return 0;
} return q.empty();
}
int main(){
scanf("%d%d%d%d",&n,&m,&k,&p);
for(int i=1;i<=n;i++) scanf("%d%d",&h[i],&a[i]);
ll l=0,r=1e18,x=0,mid;
while(l<=r) check(mid=l+r>>1)?x=mid,r=mid-1:l=mid+1;
printf("%lld\n",x);
return 0;
}