bzoj 3874: [Ahoi2014&Jsoi2014]宅男计划
三分+贪心检验 (注意check的时候,多用/法,不要炸long long)
jyy可以生存的时间 与 买的次数 成一个上凸的单峰函数
证明:
如果买的太多,光小费就给不起
如果买的太少,又不能充分利用多种食物
所以可以三分 买的次数
贪心check:
先把保质期短而且又贵的食物去掉
然后剩下的食物一定是保质期越长越贵
那就从保质期短到长一直买到不能买为止
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#define ll long long
#define mem(a,b) memset(a,b,sizeof(a))
using namespace std;
const int N=1006;
struct son
{
ll t,w;
}ji[N],q[N];
int con;
bool ok_t(son a,son b)
{
if(a.t==b.t)
return a.w<b.w;
return a.t<b.t;
}
bool ok_w(son a,son b)
{
if(a.w==b.w)
//return a.t>b.t; //wrong reason:不能a.t>b.t,因为有可能用到
return a.t<b.t;
return a.w<b.w;
}
int n;
ll M,F;
ll check(ll x)
{
ll ans=0,temp,le=M-F*x,fina,can;
if(le<0)
return 0;
for(int i=1;i<=con;++i)
{
if(le<0)
break;
temp=le/q[i].w;
/*fina=min(temp,q[i].t*x-ans );
if(fina<0) //炸long long了
while(1);*/
can=(temp+ans-1)/x+1;
if(can>q[i].t) // T_M_D 炸long long
fina=q[i].t*x-ans;
else
fina=temp;
ans+=fina;
le-=fina*q[i].w;
}
return ans;
}
ll work()
{
ll ans=0;
ll l=1,r=M/(F+q[1].w),mid,midmid,vmid,vmidmid;
while(l+15<=r)
{
mid=l+((r-l)/3);
midmid=r-((r-l)/3);
vmid=check(mid);
vmidmid=check(midmid);
if(vmid>vmidmid)
{
r=midmid;
if(ans<vmid)
ans=vmid;
}
else
{
l=mid;
if(ans<vmidmid)
ans=vmidmid;
}
}
for(ll i=l;i<=r;++i)
{
vmid=check(i);
if(ans<vmid)
ans=vmid;
}
return ans;
}
int main(){
//freopen("in.in","r",stdin);
scanf("%lld%lld%d",&M,&F,&n);
for(int i=1;i<=n;++i)
{
scanf("%lld%lld",&ji[i].w,&ji[i].t);
++ji[i].t;
}
sort(ji+1,ji+1+n,ok_w);
con=0;
q[++con]=ji[1];
for(int i=2;i<=n;++i)
if(ji[i].t>q[con].t)
q[++con]=ji[i];
//sort(q+1,q+1+con,ok_t); // 已经是按时间排序了....
cout<<work();
}