服务器
题目大意
我们需要将一份文件复制到n个服务器上,这些服务器的编号为1, 2, …, n。
首先,我们可以选择一些服务器,直接把文件复制到它们中;将文件复制到服务器i上,需要花费
ci
。对于没有通过直接复制而获得文件的服务器,它依次向后检查
i
+1,
输入格式
输入文件的第一行有一个整数
输入文件的第二行有
n
个整数,顺数第
输出格式
输出文件中只包含一个整数,即最少需要花费的费用。
样例输入
10
2 3 1 5 4 5 6 3 1 2
样例输出
18
数据范围
60%的数据中,
80%的数据中,
100%的数据中,
1
<=
最终结果可能较大,请注意选择适当的数据类型进行计算。
样例说明
题解
设
Fi
表示1~~
i
已经确定了获得文件的方式,且服务器i一定选择直接复制的的方式的最小费用。
答案即为
易得,转移方程为
Fi
=
Max
(
Fj
+
(i−j)∗(i−j−1)2
) (0 < j < i)
枚举上一个直接获得文件的服务器
j
,服务器
如果什么优化都不加,直接做这个状态转移方程,时间复杂度 On2 ,可以刷下60分。
至于100分,我们看到
(i−j)∗(i−j−1)2
这个转移的费用是二次的,如果一次就好了,随便用个单调队列维护一下就可以了,
像这种二次的费用转移,要用到斜率优化。
假设由
j
转移到
Fj
+
(i−j)∗(i−j−1)2
<
Fk
+
(i−k)∗(i−k−1)2
两边同乘2,得
2
*
拆括号,得
2
*
两边同时减去(
i2
-
i
)
把含有
两边同时除去( 2j - 2k ),( 2j - 2k )<0
2∗(Fj−Fk)+j2−k2+j−k2j−2k > i
设
Gj−GkVj−Vk > i
仔细观察左边的式子,咦,好像有点熟悉是吧?
你没有看错,那就是
传说中的圣斗士斜率!
所以当
反之,当
我们维护一个单调队列,队列里相邻的两个元素的斜率是单调递增。
当我们找到一个
发现
Gl−GpVl−Vp
<
i
,说明此时由
上面的式子也说明
完成
这时我们发现由
设函数
kd(j,k)
=
Gj−GkVj−Vk
,队列最后一个元素的下标为
r
,该队列为
则如果
kd(dr−2,dr−1)
>
kd(dr−1,dr)
,则
dr−1
一定不是最优的,可以宣布报废,将其踢出队列,为什么呢?
Reason One
从数的角度解释。
分类讨论
<1>如果
i
>
<2>如果
kd(dr−2,dr−1)
>
i
>
<3>如果
kd(dr−2,dr−1)
>
kd(dr−1,dr)
>
i
,则
综上所述,无论如何,
dr−1
都不是这三个元素中最优的,是个累赘,应该丢弃。
Reason Two
从形的角度解释。
kd(dr−2,dr−1)
>
kd(dr−1,dr)
的情况如下图
如下情况不满足队列的两两元素之间的斜率单调性,于是把 dr−1 去掉,这样新形成的最后两个元素的斜率会比原来的最后两个元素的斜率大,这样既可维护斜率单调性。
这就是斜率优化的全过程,对于每个元素,进队一次,出队一次,所以总时间复杂度为 O(n) ,可以拿100分。
Code(Pascal)
var
dl,c,f:array[0..1000000] of int64;
n,j,k,l,i,o:longint;
function jl(o:int64):int64;
begin
exit(o*(o-1) div 2);
end;
function min(a,b:int64):int64;
begin
if a<b then exit(a)
else exit(b);
end;
function ggg(j,k:int64):extended;
begin
exit((2*f[j]+j*j+j-2*f[k]-k*k-k)/(2*j-2*k));
end;
begin
readln(n);
for i:=1 to n do
read(c[i]);
for i:=1 to n do
f[i]:=jl(i)+c[i];
dl[1]:=1;
l:=1;
o:=1;
for i:=2 to n do
begin
while (O>L) AND (ggg(dl[l],dl[l+1])<i) do inc(l);
f[i]:=min(f[i],f[dl[l]]+c[i]+jl(i-dl[l]));
while (o>l) and (ggg(dl[o-1],dl[o])>ggg(dl[o],i)) do
dec(o);
inc(o);
dl[o]:=i;
end;
writeln(f[n]);
end.