luogu P4360 [CEOI2004]锯木厂选址

背景:

有些累…

题目传送门:

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

题意:

现在有 n n n棵树,每一棵树有对应的质量 a i a_i ai,且相邻的两棵树的距离是 b i b_i bi i i i i + 1 i+1 i+1的距离),现在在第 n n n棵树的位置后有一个锯木厂,但是还要修建 2 2 2个锯木厂。现在求运输所有树到锯木厂的最小费用。

思路:

恶心毒瘤题。

f i f_i fi表示在前 i i i棵树中修建了 2 2 2个锯木厂且要在 i i i修建一个锯木厂的最小费用。
A k = ∑ i = 1 k a i A_k=\sum_{i=1}^{k} a_i Ak=i=1kai
B k = ∑ i = 1 k − 1 b i B_k=\sum_{i=1}^{k-1}b_i Bk=i=1k1bi(即 1 1 1 i i i的距离)。
T j , i T_{j,i} Tj,i表示 i i i棵树的位置建一个锯木厂,从 j + 1 j+1 j+1 i i i之间的树全都运往 i i i

得到状态转移方程式:
f i = min ⁡ j = 1 i − 1 T 0 , j + T j , i + T i , n + 1 f_i=\min_{j=1}^{i-1}T_{0,j}+T_{j,i}+T_{i,n+1} fi=minj=1i1T0,j+Tj,i+Ti,n+1
这里解释一下: 0 + 1 = 1 0+1=1 0+1=1 j j j棵树运往 j j j厂; j + 1 j+1 j+1 i i i棵树运往 i i i厂; i + 1 i+1 i+1 n n n棵树运往最后一个厂 n + 1 n+1 n+1,由于 n + 1 n+1 n+1位置上没有树,所以记作 T i , n + 1 T_{i,n+1} Ti,n+1会更方便且不影响结果。

考虑 T j , i T_{j,i} Tj,i的表示。
T j , i = ∑ k = j + 1 i − 1 a k ∗ ( B i − B k ) T_{j,i}=\sum_{k=j+1}^{i-1}a_k*(B_i-B_k) Tj,i=k=j+1i1ak(BiBk)
开始一波化简。
T j , i = ∑ k = j + 1 i − 1 ( a k ∗ B i − a k ∗ B k ) T_{j,i}=\sum_{k=j+1}^{i-1}(a_k*B_i-a_k*B_k) Tj,i=k=j+1i1(akBiakBk)
T j , i = ( B i ∗ ∑ k = j + 1 i − 1 a k ) − ( ∑ k = j + 1 i − 1 B k ∗ a k ) T_{j,i}=(B_i*\sum_{k=j+1}^{i-1}a_k)-(\sum_{k=j+1}^{i-1}B_k*a_k) Tj,i=(Bik=j+1i1ak)(k=j+1i1Bkak)
s i = B i ∗ a i s_i=B_i*a_i si=Biai,则:
T j , i = B i ∗ ( A i − 1 − A j ) − ( ∑ k = j + 1 i − 1 s k ) T_{j,i}=B_i*(A_{i-1}-A_j)-(\sum_{k=j+1}^{i-1}s_k) Tj,i=Bi(Ai1Aj)(k=j+1i1sk)
S k = ∑ i = 1 n s i S_k=\sum_{i=1}^ns_i Sk=i=1nsi,则:
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)
此时时间复杂度变为 Θ ( n 2 ) \Theta(n^2) Θ(n2)

带回得: f i = ( A j − 1 B j − S j − 1 ) + ( B i ( A i − 1 − A j ) − ( S i − 1 − S j ) ) + ( B n + 1 ( A n + 1 − 1 − A i ) − ( S n + 1 − 1 − S i ) ) f_i=\Big(A_{j-1}B_j-S_{j-1}\Big)+\Big(B_i(A_{i-1}-A_j)-(S_{i-1}-S_j)\Big)+\Big(B_{n+1}(A_{n+1-1}-A_i)-(S_{n+1-1}-S_i)\Big) fi=(Aj1BjSj1)+(Bi(Ai1Aj)(Si1Sj))+(Bn+1(An+11Ai)(Sn+11Si))
f i = ( A j − 1 B i − S j − 1 ) + ( A i − 1 B i − A j B i − S i − 1 + S j ) + ( A n B n + 1 − A i B n + 1 − S n + S i ) f_i=\Big(A_{j-1}B_i-S_{j-1}\Big)+\Big(A_{i-1}B_i-A_jB_i-S_{i-1}+S_j\Big)+\Big(A_nB_{n+1}-A_iB_{n+1}-S_n+S_i\Big) fi=(Aj1BiSj1)+(Ai1BiAjBiSi1+Sj)+(AnBn+1AiBn+1Sn+Si)
T 0 i = A i − 1 B i − S i − 1 T0_i=A_{i-1}B_i-S_{i-1} T0i=Ai1BiSi1,显然可以被预处理出来,则:
f i = T 0 j + ( A i − 1 B i − A j B i − S i − 1 + S j ) + ( A n B n + 1 − A i B n + 1 − S n + S i ) f_i=T0_j+\Big(A_{i-1}B_i-A_jB_i-S_{i-1}+S_j\Big)+\Big(A_nB_{n+1}-A_iB_{n+1}-S_n+S_i\Big) fi=T0j+(Ai1BiAjBiSi1+Sj)+(AnBn+1AiBn+1Sn+Si)
得到斜率方程为: T 0 , j − T 0 , k + S j − S k A j − A k &lt; B i \frac{T_{0,j}-T_{0,k}+S_j-S_k}{A_j-A_k}&lt;B_i AjAkT0,jT0,k+SjSk<Bi
O K OK OK了。

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#define LL long long
using namespace std;
	int n;
	LL ans=(LL)(2e10);
	LL a[100010],A[100010],b[100010],B[100010],s[100010],S[100010],T0[100010],f[100010];
	int que[100010];
LL calc1(int x,int y)
{
	return A[y]-A[x];
}
LL calc2(int x,int y)
{
	return (T0[y]+S[y])-(T0[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",&a[i],&b[i]);
	for(int i=1;i<=n+1;i++)
	{
		A[i]=A[i-1]+a[i];
		B[i]=B[i-1]+b[i-1];
		s[i]=a[i]*B[i];
		S[i]=S[i-1]+s[i];
		T0[i]=A[i-1]*B[i]-S[i-1];
	}
	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]=T0[que[head]]+(A[i-1]*B[i]-A[que[head]]*B[i]-S[i-1]+S[que[head]])+(A[n]*B[n+1]-A[i]*B[n+1]-S[n]+S[i]);
		ans=min(ans,f[i]);
		while(head<tail&&calc(que[tail],i)<=calc(que[tail-1],que[tail])) tail--;
		que[++tail]=i;
	}
	printf("%lld",ans);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值