网络流经典题里餐巾计划的加强版...天数变成了$10^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); }