【洛谷P4480】餐巾计划问题

题目

题目链接:https://www.luogu.com.cn/problem/P4480
双倍经验:https://www.luogu.com.cn/problem/P2223
三倍经验:https://www.luogu.com.cn/problem/P1251
四倍经验:https://www.luogu.com.cn/problem/P2917
一个餐厅在相继的 (n) 天里,每天需用的餐巾数不尽相同。假设第 (i)((i=1, 2, ..., n)) 需要 (r_i) 块餐巾。餐厅可以在任意时刻购买新的餐巾,每块餐巾的费用为 (p) 。使用过的旧餐巾,则需要经过清洗才能重新使用。把一块旧餐巾送到清洗店A,需要等待 (m_1) 天后才能拿到新餐巾,其费用为 (c_1) ;把一块旧餐巾送到清洗店B,需要等待 (m_2) 天后才能拿到新餐巾,其费用为 (c_2) 。例如,将一块第 (k) 天使用过的餐巾送到清洗店A清洗,则可以在第 (k+m_1) 天使用。
请为餐厅合理地安排好 (n) 天中餐巾使用计划,使总的花费最小。
(nleq 2 imes 10^5)

思路

弱化版是费用流解法。加强后考虑贪心。
首先如果设买 (i) 条毛巾所需要的总花费为 (f(i)),那么 (f(i)) 是一个单峰函数。当买的毛巾小于最优解时,洗毛巾的费用就会增加;多于最优解时,买毛巾的费用就会增加。
(m_1leq m_2),如果此时 (c_1leq c_2),那么显然第二种清洗店不会有任何作用,直接让 (m_2=m_1,c_2=c_1) 即可。
所以可以三分买毛巾的数量,然后考虑如何计算买 (k) 条毛巾时的最小花费。
对于其中一天,我们肯定是依次考虑这三种操作:

  • 如果有买了没用的毛巾,那么就立刻使用。显然往后留不会更优。
  • 如果有毛巾在 (m_2) 天前使用了,那么就使用这个毛巾。也就是能慢洗就慢洗。
  • 如果有毛巾在 ([m_1,m_2)) 天前使用了,那么就使用这个毛巾。注意此时需要选择时间尽量后的毛巾使用,因为时间较前的毛巾可能再等待若干天就可以送去慢洗了。

那么维护三个队列 (q1,q2,q3),分别维护上一次使用时间在 ([1,m_1),[m_1,m_2),[m_2,+infty)) 内的毛巾。每当来到新的一天,就不断尝试将 (q1) 的队头扔到 (q2) 队尾,(q2) 队头扔到 (q3) 队尾。如果某一天所有毛巾都用完了还不够,那么返回 (+infty) 就好。
时间复杂度 (O(nlog (np)))

代码

#include <bits/stdc++.h>
#define mp make_pair
#define fi first
#define se second
using namespace std;

const int N=200010,Inf=2147483647;
int n,m1,m2,c1,c2,p,a[N];
pair<int,int> q1[N],q2[N],q3[N];

int solve(int mid)
{
	if (mid<0) return Inf;
	int h1=1,h2=1,h3=1,t1=0,t2=0,t3=0,ans=mid*p;
	for (int i=1;i<=n;i++)
	{
		for (;h1<=t1 && q1[h1].fi+m1<=i;h1++) q2[++t2]=q1[h1];
		for (;h2<=t2 && q2[h2].fi+m2<=i;h2++) q3[++t3]=q2[h2];
		int x=a[i],y=min(x,mid);
		if (mid) mid-=y,x-=y,q1[++t1]=mp(i,y);
		while (x && h3<=t3)
			if (q3[h3].se>x)
			{
				q3[h3].se-=x; ans+=x*c2;
				if (q1[t1].fi!=i) q1[++t1]=mp(i,x);
					else q1[t1].se+=x;
				x=0;
			}
			else
			{
				x-=q3[h3].se; ans+=q3[h3].se*c2;
				if (q1[t1].fi!=i) q1[++t1]=mp(i,q3[h3].se);
					else q1[t1].se+=q3[h3].se;
				h3++;
			}
		while (x && h2<=t2)
			if (q2[t2].se>x)
			{
				q2[t2].se-=x; ans+=x*c1;
				if (q1[t1].fi!=i) q1[++t1]=mp(i,x);
					else q1[t1].se+=x;
				x=0;
			}
			else
			{
				x-=q2[t2].se; ans+=q2[t2].se*c1;
				if (q1[t1].fi!=i) q1[++t1]=mp(i,q2[t2].se);
					else q1[t1].se+=q2[t2].se;
				t2--;
			}
		if (x) return Inf;
	}
	return ans;
}

int main()
{
	scanf("%d%d%d%d%d%d",&n,&m1,&m2,&p,&c1,&c2);
	m1++; m2++;
	for (int i=1;i<=n;i++)
		scanf("%d",&a[i]);
	if (m1>=m2) swap(m1,m2),swap(c1,c2);
	if (c1<=c2) m2=m1,c2=c1;
	int l=1,r=20000000,mid;
	while (r-l>=3)
	{
		mid=(l+r)>>1;
		if (solve(mid)<solve(mid+1)) r=mid;
			else l=mid;
	}
	int ans=Inf;
	for (int i=l;i<=r;i++) ans=min(ans,solve(i));
	cout<<ans;
	return 0;
}
原文地址:https://www.cnblogs.com/stoorz/p/15037062.html