餐巾计划问题(原网络流做法,但这里为三分+贪心做法)

餐巾计划问题
[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;
}
posted @ 2021-02-17 21:01  Mr_cold  阅读(157)  评论(0)    收藏  举报