餐巾计划问题(原网络流做法,但这里为三分+贪心做法)
餐巾计划问题
[USACO08NOV]Toys G
[HNOI2001]软件开发
[BJWC2018]餐巾计划问题
上面这四个题基本是同一个题,都可用接下来要说的方法解决,唯一的区别是开\(long long\)和开局两种洗的方法选择的问题,但只要在最开始操作一下就行了。
首先原本的餐巾计划问题是费用流问题,这里不再赘述,但当数据范围大起来了,就可以用三分+贪心的方法来解决,复杂度\(O(n*log_{\frac{3}{2}}n)\)
首先有三个费用产生的原因 \(a、b、c\),分别是买一个的钱,第一种消毒法的钱,第二种消毒的钱,这里默认第一种消毒法贵但快,第二种消毒法便宜但慢,如果第二种消毒法比第一种的贵,那麽就可以舍弃第二种方法了。
这里之所以会产生最小的费用,是因为我们真正需要买的餐巾数是未知的,你可以买多勤洗,也可以用多少买多少(当买的费用小于洗的费用时),因此我们不妨用\(f(x)\)老表示直接在刚开始就买\(x\)个餐巾过完所有天后所需要的费用。则\(f(x)=g(x)+a*x\),其中\(g(x) \)就是在其中消毒造成的费用,对于\(g(x)\),显然餐巾越多,消毒的次数就可以减少,慢消毒低价的次数可以增加,\(g(x)\)值就会变少,而餐巾越多\(a*x\)的值就会变大,我们可以确定这里\(f(x)\)是满足凹函数性质的,那么我们就可以三分处合适的x的范围然后解决问题。
接下来,就是求已经确定餐巾的数目\(x\),求消毒的花费。那麽首先就直接买下\(x\)个餐巾,放入一个数据结构中,然后开始对每一天进行检验,然后每当这一天结束后,将所有餐巾在全部消毒,放到一个数据结构以备再用。注意这里虽然用词是全部,但只有在真正用的时候才会算到价值。
对于每一天餐巾的来源,有最初买下的,第一种消毒方式的,第二种消毒方式的,对于最初买下的,其实我们可以直接归为第二种消毒方式的,在算最初消费时记作\(x*(a-c)\),然后在使用第二种消毒方式的时候就可以加回来。
然后由于消毒方式的使用是跟事件有关的,我们显然对每一坨毛巾设置一个二元组\((i,j)\),\(i\)是消毒的时间,\(j\)是包含的毛巾数量。
然后每个毛巾的分类有三种,一种是消毒天数到枚举的天数的时间距离小于\(n1\),一种是大于等于\(n1\)小于\(n2\),一种是大于等于\(n2\),第二种和第三种状态的餐巾可以直接使用,并且优先使用第三种状态,因为这是已经是最实惠的了。在进行状态变幻时,较早进去的那些餐巾最有可能进入到下一状态,我们在每一次遍历到新的天数时就要考虑存在变换状态的情况。。
注意,在选择第二种状态的餐巾时,如果我们选择较早放进去消毒的餐巾,它可能只差一天就可以转换到第三种状态,变得更加便宜,你却着急把它用掉,这合理吗,所以这时要贪心的从最晚进去的那一批餐巾用掉,可以使得花费更小。
综上,对于这三种状态,应该使用双端队列储存二元组来维护。
然后就是代码。
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<map>
#include<queue>
#include<bitset>
#include<vector>
#include<string>
#include<set>
using namespace std;
#define rep(i,a,b) for(register int i=a;i<=b;++i)
#define rpe(i,a,b) for(register int i=a;i>=b;--i)
#define pts putchar('\n')
#define ptc putchar(' ')
#define pb push_back
typedef long long ll;
typedef unsigned long long ull;
typedef double db;
const int inf=0x7f7f7f7f;
const ll linf=1e18;
const int maxn=2e5+9;
const int maxm=2e6+9;
const int mod=20101009;
const double PI=3.1415926;
const double eps=1e-7;
namespace IO{
ll read(){
ll a=1,b=0;char c=getchar();
while(c>'9'||c<'0'){if(c=='-')a=-1;c=getchar();}
while(c>='0'&&c<='9'){b=(b<<3)+(b<<1)+c-'0';c=getchar();}
return a*b ;
}
void print (ll x){
if(x<0) putchar('-'),x=-x;
if(x>9) print(x/10);
putchar(x%10+'0');
}
}
using namespace IO;
int d,n1,n2,c1,c2,tc;
ll t[maxn];
deque< pair<int,ll> >a,b,c;
inline void add(int day,int num){a.push_back(make_pair(day,num));}
inline void update(int day){
while(a.size()&&day-a.front().first>=n1)
b.push_back(a.front()),a.pop_front();
while(b.size()&&day-b.front().first>=n2)
c.push_back(b.front()),b.pop_front();
}
ll check(ll num){
while(a.size()) a.pop_back();
while(b.size()) b.pop_back();
while(c.size()) c.pop_back();
add(-1e9,num);
ll money=1LL*(tc-c2)*num;
rep(i,1,d){
int ned=t[i];
update(i);
while(ned&&c.size()){
if(c.front().second>ned) {
c.front().second-=ned;
money+=1LL*ned*c2;
ned=0;
}
else{
ned-=c.front().second;
money+=1LL*c2*c.front().second;
c.pop_front();
}
}
while(ned&&b.size()){
if(b.back().second>ned){
b.back().second-=ned;
money+=1LL*c1*ned;
ned=0;
}
else{
ned-=b.back().second;
money+=1LL*b.back().second*c1;
b.pop_back();
}
}
if(ned>0) return linf;
add(i,t[i]);
}
return money;
}
int main(){
ll sum=0;
d=read();
n1=read(),n2=read(),c1=read(),c2=read(),tc=read();
if(n1>n2)swap(n1,n2),swap(c1,c2);
if(c2>c1) c2=c1;
rep(i,1,d) t[i]=read(),sum+=t[i];
ll l=1,r=sum+1,m1,m2,x,y;
while(r-l>2){
m1=(2*l+r)/3,m2=(2*r+l)/3;
x=check(m1),y=check(m2);
if(x!=linf&&x<=y) r=m2;
else l=m1;
}
ll ans=linf;
rep(i,l,r) ans=min(ans,check(i));
print(ans);
return 0;
}

浙公网安备 33010602011771号