[JSOI2014]宅男计划

Description:

外卖店一共有N种食物,分别有1到N编号。第i种食物有固定的价钱Pi和保质期Si。第i种食物会在Si天后过期。JYY是不会吃过期食物的。

比如JYY如果今天点了一份保质期为1天的食物,那么JYY必须在今天或者明天把这个食物吃掉,否则这个食物就再也不能吃了。保质期可以为0天,这样这份食物就必须在购买当天吃掉。

JYY现在有M块钱,每一次叫外卖需要额外付给送外卖小哥外送费F元。

送外卖的小哥身强力壮,可以瞬间给JYY带来任意多份食物。JYY想知道,在满足每天都能吃到至少一顿没过期的外卖的情况下,他可以最多宅多少天呢?

Hint:

对于100%的数据满足0<=Si<=1018,1<=F,Pi,M<=1018,1<=N<=200

Solution:

很玄学的贪心+三分......
把题解的很多句话删掉了还是能过......

首先考虑送餐次数是个单峰的,三分即可,每次贪心时细节较多
详见代码:

// luogu-judger-enable-o2
#include <map>
#include <set>
#include <stack>
#include <cmath>
#include <queue>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#define ls p<<1 
#define rs p<<1|1
using namespace std;
typedef long long ll;
const ll mxn=1e6+5;
ll l,r,n,m,cnt,F,lmid,rmid,hd[mxn];

inline ll read() {
    char c=getchar(); ll x=0,f=1;
    while(c>'9'||c<'0') {if(c=='-') f=-1;c=getchar();}
    while(c<='9'&&c>='0') {x=(x<<3)+(x<<1)+(c&15);c=getchar();}
    return x*f;
}
inline void chkmax(ll &x,ll y) {if(x<y) x=y;}
inline void chkmin(ll &x,ll y) {if(x>y) x=y;}

struct ed {
    ll to,nxt;
}t[mxn<<1];

inline void add(ll u,ll v) {
    t[++cnt]=(ed) {v,hd[u]}; hd[u]=cnt;
}

struct food {
    ll p,s;
    friend bool operator < (food x,food y) {
        if(x.p==y.p) return x.s>y.s; 
        return x.p<y.p;
    }
}a[mxn];

ll solve(ll t) {
    ll v,ans,now,w,k,p,s;
    v=m-t*F; w=v/t; k=v-w*t; 
    ans=0; now=0;
    if(v<0) return 0;
    for(ll i=1;i<=n;++i) {
        if(a[i].s>=now&&w-a[i].p>=0) {
            p=min(a[i].s+1-now,w/a[i].p); //能买则买
            now+=p; w-=p*a[i].p;
        }
        s=i; /*记一个最多买到哪里,下面要用*/ if(w-a[i].p<0) break ;
    }
    k+=w*t; //剩下的钱
    for(ll i=s;i<=n;++i) {
        if(a[i].s>=now&&k-a[i].p>=0) {
            p=k/a[i].p; ans+=p;
            k-=p*a[i].p; //剩下来的钱也要用完
        }
    }
    return t*now+ans;
}

int main()
{
    m=read(); F=read(); n=read();
    for(ll i=1;i<=n;++i) a[i].p=read(),a[i].s=read();
    sort(a+1,a+n+1);  //贪心排序
    l=1;
    if(F!=0) r=m/F+1;
    else r=m+1;
    while(l<r) {
        lmid=l+(r-l)/3;
        rmid=r-(r-l)/3;
        if(solve(lmid)>=solve(rmid)) r=rmid-1;
        else l=lmid+1;
    }
    printf("%lld",max(solve(lmid),solve(rmid))); //取个max保险一些
    return 0;
}

posted @ 2019-03-31 10:17  cloud_9  阅读(303)  评论(0编辑  收藏  举报