food评测传送门

【题目描述】

  叫外卖是一个技术活,宅男宅女们一直面对着一个很大的矛盾,如何以有限的金钱在宿舍宅得尽量久。
     外卖店一共有 N 种食物,每种食物有固定的价钱 Pi 与保质期 Si ,保质期的意义是食物会在被买的第 Si 天后过期。譬如你在今天叫了一个 Si =1 的食物,则你必须在今天或者明天吃掉它。
     现在你有 M 元钱,每次叫外卖需要 F 元的运费,送外卖的小哥身强体壮,叫一次可以帮你带来任意份任意种食物,请问在保证每天都吃到至少一份未过期食物的前提下,你最多能宅多久?
【输入文件】
  第一行 T 表示数据组数
  对于每组数据,第一行三个整数 M F N
  接下来 N 行每行两个整数 P i S i
【输出文件】
  对于每组数据,在一行中输出一个数表示最多宅的天数
【样例输入】
  food.in
  332 5 2
  5 0
  10 2
  10 10 1
  10 10
  10 1 1
  1 5
【样例输出】
  food.out
  3
  0
  8
【数据约定】
   30%:0 <= Si <= 10,1 <= M <= 20,1 <= N <= 10
  100%:0 ≤ S i ≤ 2000000,1 ≤ M ≤ 2000000
  100%:T ≤ 10,1 ≤ F ≤ M,1 ≤ N ≤ 200,1 ≤ P i ≤ M

思路:

  觉得这题好难呀 也不知道怎么想到正解 就直接讲讲正解吧

  solution说 :“这是道构造贪心的好题”

     发现这题无从下手,但是如果知道点外卖点了多少次就好做了(我并没有觉得很好做???)

  所以我们枚举点外卖的次数 这样我们就知道总的运费了 也就知道了买食物的总钱数

  然后我们需要考虑的是两次点外卖之间的间隔 这里是一个贪心

  首先我们初始化的时候就要去掉那些又贵保质期又短的食物 保证食物的保质期与价格正相关

  先说结论吧 当点外卖的两两之间的间隔时间相等时 可以维持天数是最多的

  假如我们要维持6天 点2次外卖 那么在第一天和第四天点外卖花的钱是最少的

  因为这样子的话点的外卖中保质期最长的只需要2天就可以了 而价钱与保质期正相关

  保质期短 价钱就少

  

  我们二分每轮点外卖维持的天数

  可以预处理出点一次外卖维持一定天数的最小价钱

  所以这里就比较好判断了

  但是要注意的是 可能点一定次数的外卖并且维持一定的天数后  还有钱剩余

  这时就可以在最后一次点外卖的时候多点一些 维持更多的天数

CODE:

 #include<iostream>
#include<cstdio>
#include<cstring>
#define go(i,a,b) for(register int i=a;i<=b;i++)
#define yes(i,a,b) for(register int i=a;i>=b;i--)
#define ll long long
#define M 200+10
#define N 1000000+10
#define inf 21000000
using namespace std;
ll read()
{
int x=,y=;char c=getchar();
while(c<''||c>'') {if(c=='-') y=-;c=getchar();}
while(c>=''&&c<='') {x=(x<<)+(x<<)+c-'';c=getchar();}
return x*y;
}
ll T,n,m,f,maxn,ans,p[M],s[M],minn[N],sm[N];
void work(ll st,ll my) //step money
{
ll l=,r=maxn;
while(l<r)
{
ll mid=(l+r+)>>;
if(sm[mid]*st<=my) l=mid;
else r=mid-;
}
ans=max(ans,l*st+(my-sm[l]*st)/minn[l+]);
}
int main()
{
T=read();
while(T--)
{
m=read();f=read();n=read();
memset(minn,,sizeof(minn));
maxn=;ans=;
go(i,,n)
{
p[i]=read();s[i]=read()+;
minn[s[i]]=min(minn[s[i]],p[i]);
maxn=max(maxn,s[i]);
}
yes(i,maxn-,) minn[i]=min(minn[i],minn[i+]);
go(i,,maxn) sm[i]=sm[i-]+minn[i];
if(!f) {printf("%lld\n",m/minn[]);continue ;}
go(i,,m/f) work(i,m-i*f);
printf("%lld\n",ans);
}
}

宅男计划传送门

  和上面那题唯一不同的地方就是 数据范围

  这里的数据范围是:0<=Si<=10^18,1<=F,Pi,M<=10^18,1<=N<=200

  于是用上面的代码就会超时啦

  然后发现点外卖的次数是可以三分的(当然不是我发现的)

以下摘自gql's blog:

    可以证明,外卖的轮次与存活天数是个二次函数关系

    来下面我再来证明下Qw

    首先感性理解下,假如我现在有很多钱,我只让快递小哥来一次,我最多只能活保质期max天嘛

    然后如果小哥来两次,我依然付得起很多很多东西,我就能活2*保质期max对趴

    以此类推我们可以发现最初我的钱很多的时候我能活的天数是增加的

    但是!再举个栗子

    假如我一天点一次外卖,我就会要花很多很多外卖费,活的天数可能更少

    通过这个极端的栗子可以发现,如果我点外卖点的太频繁,我花的外卖费就很多,能用来买东西的钱就很少辣

    所以它是个单峰函数!

    有点感性,,,?

    那那那讲下另一个方法,lzq还港了一个方法,感觉似懂非懂的我大概说下QAQ

    就,我把花费分成两部分,一部分是快递费一部分是买东西

    快递费就分摊到了每一天嘛

    然后我们现在相当于是说要每一天的平均花费min

    那如果我这一轮买的东西多一个

    我的快递费平摊的对象就+1,同时的我买东西的花费也会增加

    就是从f/i+P/i变成了f/(i+1)+(P+p[i])/(i+1)

    利用反比例函数的性质可以发现开始的时候f/i这个变化值是会很大的,而相对而言的p增加的少一些,所以减少比增加多,它就会是单调增的

    但是到了后期f/i的变化量就很小了嘛,还有一点就是到了后期的pi会很大(因为我们已经是让p单调增的了

    所以增加的就比减少的多了,所以就成了单调减的

    综上,它是个单调函数

  但是并不仅仅只要三分那么简单   由于这题的 si 实在是太大了

  开不了那么大的minn[]和sm[] 所以我们就不能向之前那样子预处理了

  每次都要自己计算qwq

  优秀题解CODE:

 #include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
int n;int cnt;ll m;ll f;
struct food
{
ll cst;ll kep;
friend bool operator <(food a,food b){return a.kep<b.kep;}
}tp[],fo[];
inline ll cost(ll t)//计算存活到t的花费
{
ll res=;
for(int i=;i<=cnt;i++)
{
if(res<){return -;}
if(t>fo[i].kep){res+=fo[i].cst*fo[i].kep;t-=fo[i].kep;}
else {res+=fo[i].cst*t;return res;}
}return -;//这里是避免爆INF
}
inline ll calcd(ll pm)//计算花多少钱可以存活多少天
{
ll l=;ll r=pm;
while(l<r)//单调可以二分
{
ll mid=(l+r+)/;ll y=cost(mid);
if(y==-||y>pm){r=mid-;}else {l=mid;}
}
return r;
}
inline ll calct(ll r)//计算T
{
ll rest=m-r*f;if(rest<||rest>m){return ;}//还是避免爆longlong
ll pm=rest/r;ll k=calcd(pm);if(k==){return ;}
ll c2=cost(k+);ll c1=cost(k);if(c2==-){return r*k;}
else {rest-=c1*r;return rest/(c2-c1)+r*k;}
}
int main()
{
scanf("%lld%lld%d",&m,&f,&n);
for(int i=;i<=n;i++){scanf("%lld%lld",&tp[i].cst,&tp[i].kep);}
sort(tp+,tp+n+);tp[].kep=-;fo[].kep=-;
for(int i=;i<=n;i++)//单调栈去掉无用的东西
{while(cnt!=&&tp[i].cst<fo[cnt].cst){cnt--;}fo[++cnt]=tp[i];}
for(int i=cnt;i>=;i--){fo[i].kep-=fo[i-].kep;}//后向差分方便计算
ll l=;ll r=m/f+;//三分法求函数最值
while(r-l>)//缩小区间
{
ll x1=l+(r-l)/;ll x2=l+((r-l)*)/;
ll y1=calct(x1);ll y2=calct(x2);
if(y1<y2){l=x1;}else {r=x2;}
}
ll res=calct(l);//然后暴力枚举最大值
for(ll i=l+;i<=r;i++){res=max(res,calct(i));}
printf("%lld",res);return ;//拜拜程序~
}

外卖(food) & 洛谷4040宅男计划 三分套二分&贪心的更多相关文章

  1. Bzoj 3874: [Ahoi2014&Jsoi2014]宅男计划 三分+贪心

    3874: [Ahoi2014&Jsoi2014]宅男计划 Time Limit: 1 Sec  Memory Limit: 256 MBSubmit: 861  Solved: 336[Su ...

  2. 洛谷$P4040\ [AHOI2014/JSOI2014]$宅男计划 贪心

    正解:三分+贪心 解题报告: 传送门$QwQ$ 其实很久以前的寒假就考过了,,,但那时候$gql$没有好好落实,就只写了个二分,并没有二分套三分,就只拿到了$70pts$ #include <b ...

  3. 【BZOJ3874】[AHOI&JSOI2014]宅男计划(贪心,三分)

    [BZOJ3874][AHOI&JSOI2014]宅男计划(贪心,三分) 题面 BZOJ 洛谷 题解 大力猜想一最长的天数和购买外卖的总次数是单峰的.感性理解一下就是买\(0\)次是\(0\) ...

  4. bzoj3874&2832 [Ahoi2014]宅男计划 模拟退火,三分

    [Ahoi2014&Jsoi2014]宅男计划 Time Limit: 1 Sec  Memory Limit: 256 MBSubmit: 962  Solved: 371[Submit][ ...

  5. [luogu] P4040 [AHOI2014/JSOI2014]宅男计划(贪心)

    P4040 [AHOI2014/JSOI2014]宅男计划 题目背景 自从迷上了拼图,JYY就变成了个彻底的宅男.为了解决温饱问题,JYY不得不依靠叫外卖来维持生计. 题目描述 外卖店一共有N种食物, ...

  6. 洛谷 P2762 太空飞行计划问题 P3410 拍照【最大权闭合子图】题解+代码

    洛谷 P2762 太空飞行计划问题 P3410 拍照[最大权闭合子图]题解+代码 最大权闭合子图 定义: 如果对于一个点集合,其中任何一个点都不能到达此集合以外的点,这就叫做闭合子图.每个点都有一个权 ...

  7. 洛谷 P1114 “非常男女”计划

    To 洛谷.1114 “非常男女”计划 题目描述 近来,初一年的XXX小朋友致力于研究班上同学的配对问题(别想太多,仅是舞伴),通过各种推理和实验,他掌握了大量的实战经验.例如,据他观察,身高相近的人 ...

  8. 「AHOI2014/JSOI2014」宅男计划

    「AHOI2014/JSOI2014」宅男计划 传送门 我们首先要发现一个性质:存货天数随买食物的次数的变化类似于单峰函数. 具体证明不会啊,好像是二分加三分来证明?但是没有找到明确的严格证明. 感性 ...

  9. 【洛谷5008】逛庭院(Tarjan,贪心)

    [洛谷5008]逛庭院(Tarjan,贪心) 题面 洛谷 题解 如果图是一个\(DAG\),我们可以任意选择若干个不是入度为\(0\)的点,然后把它们按照拓扑序倒序删掉,不难证明这样一定是合法的. 现 ...

随机推荐

  1. js 函数讲解

    函数 来自<JavaScript 标准参考教程(alpha)>,by 阮一峰 目录 概述 函数的声明 函数的重复声明 圆括号运算符,return 语句和递归 第一等公民 函数名的提升 不能 ...

  2. 红帽旗下Linux的版本说明RedHat、CentOS、Fedora、OEL等

    简单总结一下RedHat.CentOS.Fedora Core区别关系: RedHat: 红帽已经被IBM 340亿刀收购了,但是红帽依旧发型自己的RedHat enterprise linux 版本 ...

  3. sys下gpio操作

    gpio_operation 通过/sys/文件接口操作IO端口 GPIO到文件系统的映射 * 控制GPIO的目录位于/sys/class/gpio * /sys/class/gpio/export文 ...

  4. NLP & 中文分词

    NLP & 中文分词 中文分词 (Word Segmentation, WS) 指的是将汉字序列切分成词序列. 中文自然语言处理系统 https://www.ltp-cloud.com/int ...

  5. JavaWeb基础【1】—— Tomcat

    此笔记是学习黑马程序员JavaWeb系列视频的课堂笔记. 感谢黑马程序员. 一.Tomcat概述 Tomcat服务器由Apache提供,开源免费.由于Sun和其他公司参与到了Tomcat的开发中,所以 ...

  6. Java中ArrayList的fori和foreach效率比较

    1. list的元素为Integer [代码实例1] public static void main(String[] args) { List<Integer> list = new A ...

  7. Qt4问题集锦

    一.Qt程序发布后加载的jpg.jpeg图片不可见 问题的提出: 最近在客户机器上部署安装QT编写的软件,发现只要是jpg.jpeg格式的图片都无法显示出来.最后发现必须按如下述步骤才能显示jpg.j ...

  8. Yarn源码分析1(Hadoop2.7.2)

    在Hadoop中,调度框架YARN(Yet Another Resource Negotiater)是基于事件的,调度的是MapReduce的Application.Application有一系列的状 ...

  9. 【bzoj2034】 2009国家集训队—最大收益

    http://www.lydsy.com/JudgeOnline/problem.php?id=2034 (题目链接) 题意 n个任务,每个任务只需要一个时刻就可以完成,完成后获得${W_i}$的收益 ...

  10. Hibernate中常见问题 No row with the given identifier exists问题

    收集:Hibernate中常见问题 No row with the given identifier exists问题的原因及解决 2007年11月21日 15:02:00 eyejava 阅读数:2 ...