三分+贪心 [Ahoi2014]宅男计划

传送门
一看显然是贪心(首先要把所有性价比低的食品扔掉,也就是保质期短还贵的东西)。但接下来我就懵了。。。居然要三分
据说没什么人能证出来。。(据说出题人要别人猜结论。。)既然说是,那我就信了。→_→
三分总共要多少次外卖。然后从便宜的食品开始买(把过期的过掉)。那么该如何记录呢。既然我们知道要来多少次,那么每一次买的都是最优的话,那么每次买的是一样的。这样我们可以记录下已经把前多少天的食物买齐了,向后加天数即可。答案也就是来的次数×一次撑的天数。
有一个细节:我们把买的食物在每一个周期都买了n份,但可能导致保质期充足,剩下的钱足够买几个但不够把所有周期都买过来一遍。这样不同周期就会有变动。这时只需要把撑的总天数加上即可,而每个周期到底多少天了,并不用加,因为如果进行了“填缝”,那么剩余的钱不会再足以买后面的物品了。但如果只是因为保质期不够了呢?因为是按价格排的序,而且筛去了性价比不高的物品,所以后面的要么不能再买了,要么能够重新覆盖刚刚填的缝隙,所以就没必要增加了。感谢神犇GXY

#pragma GCC optimize("O3")
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
int n,tot;ll f,m,ans;
struct node{ll p,t;}b[205],a[205];
inline bool cmp(node x,node y){return x.t==y.t?x.p<y.p:x.t>y.t;}
inline bool cmp2(node x,node y){return x.p<y.p;}
inline ll check(ll x)
{
    ll now=m-x*f,s=0,sum=0,tmp;
    if(now<0)return 0;
    for(int i=1;i<=tot;i++)
    {
        ll t=a[i].t,p=a[i].p;
        if(s<=t){tmp=min(t-s+1,now/(p*x));now-=p*tmp*x;s+=tmp,sum+=tmp*x;}
        if(s<=t){tmp=min(x,now/p);now-=p*tmp;sum+=tmp;}
    }
    return sum;
}
int main()
{
    scanf("%lld%lld%d",&m,&f,&n);
    for(int i=1;i<=n;i++)scanf("%lld%lld",&b[i].p,&b[i].t);
    sort(b+1,b+n+1,cmp);
    for(int i=1;i<=n;i++)
    {
        if(i==1){a[++tot]=b[1];continue;}
        if(b[i].p>=b[i-1].p)continue;
        a[++tot]=b[i];
    }
    sort(a+1,a+tot+1,cmp2);
    ll l=1,mid,mmid,r=m/(f+a[1].p),sum;
    ll s1,s2;
    while(l<=r)
    {
        sum=r-l+1;mid=l+sum/3;mmid=l+sum*2/3;
        s1=check(mid);s2=check(mmid);
        if(s1>=s2){ans=max(ans,s1),r=mmid-1;}
        else{ans=max(ans,s2);l=mid+1;}
    }
    cout<<ans;
}
原文地址:https://www.cnblogs.com/QTY2001/p/7632640.html