bzoj5164:餐厅计划问题(三分+贪心)

Author Avatar
Sakits 3月 03, 2018

题解

网络流经典题里餐巾计划的加强版…天数变成了10510^5,那就不能用费用流做了…

考虑费用流的时候,单位费用随流量的增加而减少,也就是说费用其实是个单峰(下凸)函数。

那么可以三分要买的餐巾个数,求费用可以用贪心。

新买的没用就用新买的,否则能慢洗的慢洗,不能慢洗的拿最晚的快洗后可以当天用的去快洗。

有一个错误的贪心是慢洗和快洗的都拿最早的,这样可能会导致快洗的本来可以洗时间更晚一些的,却洗了比较早的餐巾,而这些餐巾本可以在接下来的一天里通过慢洗来省下更多的钱。

代码

#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<algorithm>
#define ll long long
using namespace std;
const int maxn=500010, inf=1e9;
struct poi{int tim, rest;}q1[maxn], q2[maxn], q3[maxn];
int d, n1, n2, c1, c2, tc, ans=inf;
int t[maxn];
inline void read(int &k)
{
    int f=1; k=0; char c=getchar();
    while(c<'0' || c>'9') c=='-'&&(f=-1), c=getchar();
    while(c<='9' && c>='0') k=k*10+c-'0', c=getchar();
    k*=f;    
} 
inline int f(int x)
{
    int ans=(tc-c2)*x;
    int l1, r1, l2, r2, l3, r3;
    l1=l2=l3=r1=1; r2=r3=0;
    q1[1].tim=-inf; q1[1].rest=x;
    for(int i=1;i<=d;i++)
    {
        while(l1<=r1 && q1[l1].tim+n1<=i) q2[++r2]=q1[l1++];
        while(l2<=r2 && q2[l2].tim+n2<=i) q3[++r3]=q2[l2++];
        int rest=t[i];
        while(rest)
        {
            if(l3<=r3)
            {
                if(q3[r3].rest>=rest) q3[r3].rest-=rest, ans+=rest*c2, rest=0;
                else ans+=q3[r3].rest*c2, rest-=q3[r3--].rest;
            }
            else if(l2<=r2)
            {
                if(q2[r2].rest>=rest) q2[r2].rest-=rest, ans+=rest*c1, rest=0;
                else ans+=q2[r2].rest*c1, rest-=q2[r2--].rest;
            }
            else return inf;
        }
        q1[++r1].tim=i; q1[r1].rest=t[i];
    }
    return ans;
}
int main()
{
    read(d); read(n1); read(n2); read(c1); read(c2); read(tc);
    int l=1, r=0;
    for(int i=1;i<=d;i++) read(t[i]), r+=t[i];
    if(n1>n2) swap(n1, n2), swap(c1, c2);
    if(c1<c2) n2=n1, c2=c1;
    while(r-l>4)
    {
        int mid1=l+(r-l)/3, mid2=l+(r-l)*2/3;
        if(f(mid1)==inf || f(mid1)>f(mid2)) l=mid1;
        else r=mid2;
    }
    for(int i=l;i<=r;i++) ans=min(ans, f(i));
    printf("%d\n", ans);
}