- 共有(n)天,第(i)天需要(a_i)个餐盘,每天餐盘用完后会变脏,需清洗后才能再次使用。
- 你每花(p)的代价可以购买一个餐盘,每个餐盘用完后可以花(d1)天、(c1)的代价或是(d2)天、(c2)的代价送去清洗。
- 求支撑到这(n)天结束的最小代价。
- (nle2 imes10^5)
网络流?
此题的一个数据弱化版:【洛谷1251】餐巾计划问题(隶属于网络流二十四题)。
三分购买餐盘数
显然购买餐盘的数量过少或过多都一定越来越不优,因此我们发现这是一个可以三分的东西。
先注意一个问题,这题没有限制快洗一定比慢洗耗钱,所以我们要让慢洗花费向快洗划分取较小值。
这样一来,就能确定我们的贪心策略:
- 购买的餐盘一开始就全用掉,因为越早将餐盘引入就能得到越充分的利用。
- 能慢洗肯定贪心地选择慢洗,实在不行才选择快洗,如果连快洗都不行就无解。
因此我们只要用两个变量分别记录剩余的购买餐盘数、已经可以选择慢洗的餐盘数,然后开两个队列维护只可以选择快洗的餐盘序列和连快洗都不行的餐盘序列。
对于某一天,首先尽可能购买,其次尽可能选择慢洗,再次我们从快洗队列的尾部开始选择快洗(迟出现的也会更迟才能变得可以慢洗,肯定相对不优)。
注意每次把无法快洗队列开头变得可以快洗的元素加入快洗队列,把快洗队列开头变得可以慢洗的元素计入可慢洗餐盘数并弹出。
代码:(O(nlogn))
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 200000
#define INF (int)1e9
using namespace std;
int n,p,d1,c1,d2,c2,a[N+5];
namespace FastIO
{
#define FS 100000
#define tc() (FA==FB&&(FB=(FA=FI)+fread(FI,1,FS,stdin),FA==FB)?EOF:*FA++)
char oc,FI[FS],*FA=FI,*FB=FI;
Tp I void read(Ty& x) {x=0;W(!isdigit(oc=tc()));W(x=(x<<3)+(x<<1)+(oc&15),isdigit(oc=tc()));}
}using namespace FastIO;
struct Data {int v,c;};deque<Data> q1,q2;I int Calc(RI x)//强制购买x个餐盘
{
RI i,A,o,y=0,t=x*p;for(q1.clear(),q2.clear(),i=1;i<=n;++i)
{
A=a[i],o=min(A,x),A-=o,x-=o,q2.push_back((Data){i,o});if(!A) continue;//首先尽可能购买
W(!q2.empty()&&q2.front().v+d1<=i) q1.push_back(q2.front()),q2.pop_front();//把无法快洗队列开头变得可以快洗的元素加入快洗队列
W(!q1.empty()&&q1.front().v+d2<=i) y+=q1.front().c,q1.pop_front();//把快洗队列开头变得可以慢洗的元素计入可慢洗餐盘数并弹出
o=min(A,y),A-=o,y-=o,t+=o*c2,q2.push_back((Data){i,o});if(!A) continue;//其次尽可能选择慢洗
W(A&&!q1.empty()) o=min(A,q1.back().c),t+=o*c1,A-=o,!(q1.back().c-=o)&&(q1.pop_back(),0),q2.push_back((Data){i,o});//再次从快洗队列的尾部开始快洗
if(A) return INF+x*p;//如果无法达成
}return t;
}
int main()
{
RI i;read(n),read(d1),read(d2),read(c1),read(c2),read(p),d1>d2&&(swap(d1,d2),swap(c1,c2),0),c2=min(c2,c1);//慢洗花费向快洗取较小值
RI l=0,r=0;for(i=1;i<=n;++i) read(a[i]),l=max(l,a[i]),r+=a[i];
RI m1,m2;W(r-l>2) m1=l+(r-l)/3,m2=l+(r-l)/3*2,Calc(m1)<Calc(m2)?r=m2:l=m1;//三分答案
RI t=INF;for(i=l;i<=r;++i) t=min(t,Calc(i));return printf("%d
",t),0;
}