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. 钉钉开发c#帮助类 获取用户信息 DingHelper.cs

    using System;using System.Collections.Generic;using System.Configuration;using System.Linq;using Sys ...

  2. C++ 查看预处理后的源文件(查看真实代码)

    gcc -E filename.cpp 会生成 filename.cpp 的预处理文件,这样就能看到宏展开后的代码,用于理解和调试宏非常有帮助.   http://www.qtdebug.com/cp ...

  3. es6 很简单

    es6出了许多好的,优秀的特性.下面列举一些常用的 其实这些特性都很好理解,一两句话就可以表达出来看.主要是对旧的写法的一种改进. function  加了一些语言糖,传参更方便 class      ...

  4. BeautifulSoup的find()和findAll()

    BeautifulSoup的提供了两个超级好用的方法(可能是你用bs方法中最常用的).借助这两个函数,你可以通过表现的不同属性轻松过滤HTML(XML)文件,查找需要的标签组或单个标签. 首先find ...

  5. NOI2010~NOI2018选做

    [NOI2010] [NOI2010]海拔 高度只需要0/1,所以一个合法方案就是一个割,平面图求最小割. [NOI2010]航空管制 反序拓扑排序,每次取出第一类限制最大的放置,这样做答案不会更劣. ...

  6. 【BZOJ1835】基站选址(线段树)

    [BZOJ1835]基站选址(线段树) 题面 BZOJ 题解 考虑一个比较暴力的\(dp\) 设\(f[i][j]\)表示建了\(i\)个基站,最后一个的位置是\(j\)的最小代价 考虑如何转移\(f ...

  7. BZOJ 1013 | 一份写了一堆注释的高斯消元题解

    题意 给出\(n\)维直角坐标系中\(n + 1\)个点的坐标,它们都在一个\(n\)维球面上,求球心坐标. 题解 设球面上某两个点坐标为\((a_1, a_2, ... a_n)\)和\((b_1, ...

  8. 【Cf #291 B】R2D2 and Droid Army(二分,线段树)

    因为题目中要求使连续死亡的机器人最多,令人联想到二分答案. 考虑如何检验这之中是否存在一段连续的长度为md的区间,其中花最多k步使得它们都死亡. 这个条件等价于区间中m个最大值的和不超过k. 枚举起点 ...

  9. 洛谷P4233 射命丸文的笔记 【多项式求逆】

    题目链接 洛谷P4233 题解 我们只需求出总的哈密顿回路个数和总的强联通竞赛图个数 对于每条哈密顿回路,我们统计其贡献 一条哈密顿回路就是一个圆排列,有\(\frac{n!}{n}\)种,剩余边随便 ...

  10. 洛谷 P1446 [HNOI2008]Cards 解题报告

    P1446 [HNOI2008]Cards 题目描述 小春现在很清闲,面对书桌上的\(N\)张牌,他决定给每张染色,目前小春只有\(3\)种颜色:红色,蓝色,绿色.他询问Sun有多少种染色方案,Sun ...