luogu P2120 [ZJOI2007]仓库建设

背景:

好像比上一题更简单。反正秒想到。

题目传送门:

https://www.luogu.org/problemnew/show/P2120

题意:

基本同[CEOI2004]锯木厂选址。
只是可以建多个仓库罢了,且有建仓库费用( c i c_i ci)罢了。

思路:

基本同[CEOI2004]锯木厂选址。
我们可以不用关心建了多少个仓库。
f i f_i fi表示第 i i i厂建仓库时前 i i i个厂的最小费用。其余变量定义见上一题。

得到: f i = min ⁡ j = 1 i − 1 f j + T j , i + c i f_i=\min_{j=1}^{i-1}f_j+T_{j,i}+c_i fi=minj=1i1fj+Tj,i+ci
由上一题推得: T j , i = B i ∗ ( A i − 1 − A j ) − ( S i − 1 − S j ) T_{j,i}=B_i*(A_{i-1}-A_j)-(S_{i-1}-S_j) Tj,i=Bi(Ai1Aj)(Si1Sj)
因此: f i = min ⁡ j = 1 i − 1 f j + B i ∗ ( A i − 1 − A j ) − ( S i − 1 − S j ) + c i f_i=\min_{j=1}^{i-1}f_j+B_i*(A_{i-1}-A_j)-(S_{i-1}-S_j)+c_i fi=minj=1i1fj+Bi(Ai1Aj)(Si1Sj)+ci
得到斜率方程: f j − f k + S j − S k A j − A k < B i \frac{f_j-f_k+S_j-S_k}{A_j-A_k}<B_i AjAkfjfk+SjSk<Bi
即可。

代码:

#include
#include
#include
#define LL long long
using namespace std;
	int n;
	LL a[1000010],A[1000010],B[1000010],c[1000010],s[1000010],S[1000010],f[1000010];
	int que[1000010];
LL calc1(int x,int y)
{
	return A[y]-A[x];
}
LL calc2(int x,int y)
{
	return (f[y]+S[y])-(f[x]+S[x]);
}
double calc(int x,int y)
{
	return (double)(calc2(x,y))/(double)(calc1(x,y));
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%lld %lld %lld",&B[i],&a[i],&c[i]);
		A[i]=A[i-1]+a[i];
		s[i]=a[i]*B[i];
		S[i]=S[i-1]+s[i];
	}
	int head=1,tail=1;
	for(int i=1;i<=n;i++)
	{
		while(head<tail&&calc(que[head],que[head+1])<=(double)B[i]) head++;
		f[i]=f[que[head]]+A[i-1]*B[i]-A[que[head]]*B[i]-S[i-1]+S[que[head]]+c[i];
		while(head<tail&&calc(que[tail],i)<=calc(que[tail-1],que[tail])) tail--;
		que[++tail]=i;
	}
	printf("%lld",f[n]);
}

你可能感兴趣的:(#,斜率优化,#,单调队列,斜率优化,单调队列)