CF505E Mr. Kitayuta vs. Bamboos 二分+贪心

二分最大值,然后考虑如何处理.     

正着做很难,因为每次减掉 $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\n",ans); 
    return 0;
}

  

posted @ 2020-07-14 14:23  EM-LGH  阅读(142)  评论(0编辑  收藏  举报