正解:贪心
解题报告:
传送门$QwQ$
其实很久以前的寒假就考过了,,,但那时候$gql$没有好好落实,就只写了个二分,并没有二分套三分,就只拿到了$70pts$
#include <bits/stdc++.h> using namespace std; #define ll long long #define rp(i,x,y) for(register ll i=x;i<=y;++i) #define my(i,x,y) for(register ll i=x;i>=y;--i) const ll N=10000+10; ll m,f,n,p[N],s[N],mnv[N*200],mxs,ans,sum[N*200]; inline ll read() { char ch=getchar();ll x=0;bool y=1; while(ch!='-' && (ch>'9' || ch<'0'))ch=getchar(); if(ch=='-')ch=getchar(),y=0; while(ch>='0' && ch<='9')x=(x<<1)+(x<<3)+(ch^'0'),ch=getchar(); return y?x:-x; } void fd(ll v,ll tot) { ll l=0,r=mxs; while(l<r) { int mid=(l+r+1)>>1; if((ll)sum[mid]*tot<=(ll)v)l=mid; else r=mid-1; } ans=max((ll)ans,l*tot+(v-sum[l]*tot)/mnv[l+1]); } void work() { for(register ll i=f,tot=1;i<=m;i+=f,++tot)fd(m-i,tot); printf("%lld\n",ans); } int main() { // freopen("food.in","r",stdin); // freopen("food.out","w",stdout); m=read();f=read();n=read(); memset(mnv,127/3,sizeof(mnv));mxs=ans=0; rp(i,1,n){p[i]=read();s[i]=read()+1;mnv[s[i]]=min(mnv[s[i]],p[i]);mxs=max(s[i],mxs);} my(i,mxs-1,1)mnv[i]=min(mnv[i+1],mnv[i]);rp(i,1,mxs)sum[i]=sum[i-1]+mnv[i]; if(f==0){printf("%lld\n",m/mnv[1]);return 0;}work(); }
然后再套个三分就写完了$QwQ$
还是正儿八经写下题解趴$QwQ$(其实是从,考试总结蒯过来的$QwQ$
首先显然可以处理一下使得所有物品的花费是随保质期单调增的(单调栈走一波就成
然后枚举点了几轮外卖,就可以算出外卖费,就可以二分求出可以活几天
关于二分的正确性很容易证明
首先我们可以得出对于每一轮的最优天数一定是接近的
来我证明下
假如现在有两种方案
第一种是第一轮买$n$个第二轮买$n+100$个
第二种是第一轮买$n+50$个第二轮买$n+50$个
首先前$n$个吃的一定是一样的(显然是先吃花费低保质期低,这个不用证明趴,,,?
然后第一种第二轮的$n$个之后的那$50$个也一定是和第二种的$n$个之后的那$50$个是一样的
这样我们的比较就变成了剩下第一种第二轮$n+50$个之后的$50$个第二种第二轮$n$之后的$50$个
因为我们已经通过处理使得花费随保质期单调增了
所以买$n+50$个之后的$50$个一定花费不比$n$个之后的$50$个花费少
所以我们可以证明最优天数要么是$T$要么是$T+1$
所以我们就可以二分这个$T$,就欧克了
但是,如果在洛谷上做这道题这个方法就是过不去的了$QAQ$只有$70pts$(好像是$80pts$我写锅了$QwQ$
说下还要优化哪儿
就是,看一下上面的解法,会发现唯一可以优化的地方就是,我外卖的轮次是枚举的
怎么优化呢?三分法
可以证明,外卖的轮次与存活天数是个二次函数关系
来下面我再来证明下$QwQ$
首先如果不收钱,这就是个上凸函数.然后发现要钱了,就减去一个下凸函数
上凸减下凸依然是上凸,所以这是个上凸函数,可三分$QwQ$
然后之后的步骤就和上面那个$70pts$的是一样的辣!
(对了,,,实际是用三分套二分做的$QwQ$.因为二分套三分会爆$ll$好像$kk$