题目
分析
首先,稍有常识的人都会看出,这道题其实就是网络流24题中“餐巾问题”的加强版。
“餐巾问题”的标准做法是费用流:每天拆成两点(i,i’),然后S向i’连容量Ti(第i天用量),费用0的边,代表当天的脏餐巾。当然还有别的边,但这个是最重要的。
其实“餐巾问题”还有一个枚举+贪心的做法。
假设我们已经确定了一共新买多少餐巾。那我们一开始就把这些餐巾全买下来,然后从第1天到第N天逐天进行模拟:
①如果还有一些新买的餐巾,就用掉它们。
②如果不够,从后往前枚举第i-slow~1天,慢洗这些天的脏餐巾。(慢洗需要slow天)
③如果还不够,从后往前枚举第i-fast~i-slow+1天,快洗这些天的脏餐巾。(快洗需要fast天)
这个做法的正确性比较显然。就是尽量慢洗,不行再尽量快洗,并且尽量使用靠后日期产生的脏餐巾。
对“餐巾问题”而言,我们只需要暴力枚举总共买多少餐巾即可。
但对于这里N<=10^5的情况而言,暴力枚举并不行。
怎么办呢?
答案是:假如把新买x个的花费视作一个函数f(x),那么它是单峰的(严格来讲是下凸)。
为什么呢?回到我们的网络流模型。一开始我们会把代表脏餐巾的边流满(费用为0),然后每流一次,就代表多买一些餐巾。而每次找出的最短路长度是在不断上升的,换言之,f(x)的一阶导数在不断上升(这个‘一阶导数’可以粗略地认为是每次找出的最短路长度)。于是f(x)就是下凸的了。
所以三分x就可以解决N<=10^5的这道题了。
代码
#include#include #include #include #include using namespace std; typedef long long LL; const int SIZEN=100010; const int INF=0x7fffffff/2; int N; int price; int slow_cost,slow_wait,fast_cost,fast_wait; int need[SIZEN]={0}; deque > can_slow,can_fast,late;//first是日期,second是个数 int calc(int buy){//购买buy个 int ans=buy*price; can_fast.clear();can_slow.clear();late.clear(); for(int i=1;i<=N;i++){ while(!late.empty()&&late.front().first<=i-fast_wait){ can_fast.push_back(late.front()); late.pop_front(); } while(!can_fast.empty()&&can_fast.front().first<=i-slow_wait){ can_slow.push_back(can_fast.front()); can_fast.pop_front(); } int now=need[i]; int t=min(now,buy); now-=t,buy-=t; while(now&&!can_slow.empty()){ t=min(now,can_slow.back().second); ans+=t*slow_cost; now-=t,can_slow.back().second-=t; if(!can_slow.back().second) can_slow.pop_back(); } while(now&&!can_fast.empty()){ t=min(now,can_fast.back().second); ans+=t*fast_cost; now-=t,can_fast.back().second-=t; if(!can_fast.back().second) can_fast.pop_back(); } if(now) return INF; late.push_back(make_pair(i,need[i])); } return ans; } void work(void){ int l=0,r=0; for(int i=1;i<=N;i++) r+=need[i]; while(r-l>2){ int m1=(2*l+r)/3,m2=(l+2*r+2)/3; int f1=calc(m1),f2=calc(m2); if(f1>=f2) l=m1; else r=m2; } int ans=INF; for(int i=l;i<=r;i++) ans=min(ans,calc(i)); printf("%d\n",ans); } void read(void){ scanf("%d",&N); scanf("%d%d",&fast_wait,&slow_wait); scanf("%d%d",&fast_cost,&slow_cost); if(fast_wait>slow_wait){ swap(fast_wait,slow_wait); swap(fast_cost,slow_cost); } if(slow_cost>fast_cost){ slow_wait=fast_wait; slow_cost=fast_cost; } scanf("%d",&price); for(int i=1;i<=N;i++) scanf("%d",&need[i]); } int main(){ freopen("toy.in","r",stdin); freopen("toy.out","w",stdout); read(); work(); return 0; }