背景:
有些累…
题目传送门:
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=1k−1bi(即
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=1i−1T0,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+1i−1ak∗(Bi−Bk)
开始一波化简。
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+1i−1(ak∗Bi−ak∗Bk)
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=(Bi∗∑k=j+1i−1ak)−(∑k=j+1i−1Bk∗ak)
设
s
i
=
B
i
∗
a
i
s_i=B_i*a_i
si=Bi∗ai,则:
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∗(Ai−1−Aj)−(∑k=j+1i−1sk)
设
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∗(Ai−1−Aj)−(Si−1−Sj)
此时时间复杂度变为
Θ
(
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=(Aj−1Bj−Sj−1)+(Bi(Ai−1−Aj)−(Si−1−Sj))+(Bn+1(An+1−1−Ai)−(Sn+1−1−Si))
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=(Aj−1Bi−Sj−1)+(Ai−1Bi−AjBi−Si−1+Sj)+(AnBn+1−AiBn+1−Sn+Si)
设
T
0
i
=
A
i
−
1
B
i
−
S
i
−
1
T0_i=A_{i-1}B_i-S_{i-1}
T0i=Ai−1Bi−Si−1,显然可以被预处理出来,则:
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+(Ai−1Bi−AjBi−Si−1+Sj)+(AnBn+1−AiBn+1−Sn+Si)
得到斜率方程为:
T
0
,
j
−
T
0
,
k
+
S
j
−
S
k
A
j
−
A
k
<
B
i
\frac{T_{0,j}-T_{0,k}+S_j-S_k}{A_j-A_k}<B_i
Aj−AkT0,j−T0,k+Sj−Sk<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);
}