PS:CH 5A01
题目
机器上有 N N N个需要处理的任务,这些任务被标号为 1 1 1到 N N N,顺序不能变。这 N N N个任务被分成若干批,每批包含相邻的若干任务。从时刻0开始,这些任务被分批加工,第 i i i个任务单独完成所需的时间是 T i T_i Ti。在每批任务开始前,机器需要启动时间 S S S,而完成这批任务所需的时间是各个任务需要时间的总和。注意,同一批任务将在同一时刻完成。每个任务的费用是它的完成时刻乘以一个费用系数 F i F_i Fi。请确定一个分组方案,使得总费用最小。
分析
首先按照费用提前计算的思想,最后可以得到状态转移方程
f
[
i
]
=
min
{
f
[
j
]
+
s
u
m
t
[
i
]
∗
(
s
u
m
c
[
i
]
−
s
u
m
c
[
j
]
)
+
S
∗
(
s
u
m
c
[
n
]
−
s
u
m
c
[
j
]
)
}
O
(
n
2
)
f[i]=\min\{f[j]+sumt[i]*(sumc[i]-sumc[j])+S*(sumc[n]-sumc[j])\}O(n^2)
f[i]=min{f[j]+sumt[i]∗(sumc[i]−sumc[j])+S∗(sumc[n]−sumc[j])}O(n2)
把仅与
i
i
i有关的单独提取出来可以得到
f
[
i
]
=
min
{
f
[
j
]
−
(
s
+
s
u
m
t
[
i
]
)
∗
s
u
m
c
[
j
]
}
+
s
u
m
t
[
i
]
∗
s
u
m
c
[
i
]
+
S
∗
s
u
m
c
[
n
]
f[i]=\min\{f[j]-(s+sumt[i])*sumc[j]\}+sumt[i]*sumc[i]+S*sumc[n]
f[i]=min{f[j]−(s+sumt[i])∗sumc[j]}+sumt[i]∗sumc[i]+S∗sumc[n]
把有关
j
j
j的值看作变量其它当作常量得到
f
[
j
]
=
f
[
j
]
−
(
s
+
s
u
m
t
[
i
]
)
∗
s
u
m
c
[
j
]
−
s
u
m
t
[
i
]
∗
s
u
m
c
[
i
]
−
S
∗
s
u
m
c
[
n
]
f[j]=f[j]-(s+sumt[i])*sumc[j]-sumt[i]*sumc[i]-S*sumc[n]
f[j]=f[j]−(s+sumt[i])∗sumc[j]−sumt[i]∗sumc[i]−S∗sumc[n]
可以发现这是斜率优化,在本题维护的是下凸壳,所以可以用单调队列解决(斜率满足单调性)
代码
#include <cstdio>
typedef long long ll; int n,s,q[300001];
ll f[300001],st[300001],sc[300001];
ll in(){
ll ans=0; int f=1; char c=getchar();
while ((c<48||c>57)&&c!='-') c=getchar();
if (c=='-') c=getchar(),f=-f;
while (c>47&&c<58) ans=ans*10+c-48,c=getchar();
return ans*f;
}
int main(){
n=in(); s=in(); q[1]=0;
for (int i=1;i<=n;i++)
st[i]=st[i-1]+in(),sc[i]=sc[i-1]+in();
f[0]=0; int l=1,r=1;
for (int i=1;i<=n;i++){
while (l<r&&(f[q[l+1]]-f[q[l]])<=(s+st[i])*(sc[q[l+1]]-sc[q[l]])) l++;//队头可以更优
f[i]=f[q[l]]-(s+st[i])*sc[q[l]]+st[i]*sc[i]+s*sc[n];//dp
while (l<r&&(f[q[r]]-f[q[r-1]])*(sc[i]-sc[q[r]])>=(f[i]-f[q[r]])*(sc[q[r]]-sc[q[r-1]])) r--;//队尾不满足单调递增
q[++r]=i;
}
return !printf("%lld",f[n]);
}