我们需要将一份文件复制到n个服务器上,这些服务器的编号为1, 2, …, n。
首先,我们可以选择一些服务器,直接把文件复制到它们中;将文件复制到服务器i上,需要花费 ci 。对于没有通过直接复制而获得文件的服务器,它依次向后检查 i +1, i +2, …直到找到一台服务器 j ( j 中的文件是通过直接复制得到的),于是i从j得到该文件,费用为 j – i ( j > i )。另外, n 中的文件必须是通过直接复制得到的,因为它不可能间接获得文件。现在让所有服务器都获得文件,求最小费用。
输入文件的第一行有一个整数 n ,表示服务器的数目。
输入文件的第二行有 n 个整数,顺数第 i 个表示 ci ,表示 i 服务器上直接复制文件的费用。
输出文件中只包含一个整数,即最少需要花费的费用。
10
2 3 1 5 4 5 6 3 1 2
18
60%的数据中, 1 <= n <=1000
80%的数据中, 1 <= ci <=50
100%的数据中, 1 <= ci <=1 000 000 000, 1 <= n <=1 000 000
最终结果可能较大,请注意选择适当的数据类型进行计算。
设 Fi 表示1~~ i 已经确定了获得文件的方式,且服务器i一定选择直接复制的的方式的最小费用。
答案即为 Fn 。
易得,转移方程为 Fi = Max ( Fj + (i−j)∗(i−j−1)2 ) (0 < j < i)
枚举上一个直接获得文件的服务器 j ,服务器 j +1到服务器 i -1全部选择间接方式,他们间接复制的费用为 (i−j)∗(i−j−1)2 ,转移方程就出来了。
如果什么优化都不加,直接做这个状态转移方程,时间复杂度 On2 ,可以刷下60分。
至于100分,我们看到 (i−j)∗(i−j−1)2 这个转移的费用是二次的,如果一次就好了,随便用个单调队列维护一下就可以了,
像这种二次的费用转移,要用到斜率优化。
假设由 j 转移到 i 比由 k 转移到 i 更优,( j < k )则有
Fj + (i−j)∗(i−j−1)2 < Fk + (i−k)∗(i−k−1)2
两边同乘2,得
2 * Fj + (i−j)∗(i−j−1) < 2 * Fk + (i−k)∗(i−k−1)
拆括号,得
2 * Fj + i2 + j2 - 2ij - i + j < 2 * Fk + i2 + k2 - 2ik - i + k
两边同时减去( i2 - i )
2 * Fj + j2 - 2ij + j < 2 * Fk + k2 - 2ik + k
把含有 i 的项全部移到右边,剩下不含有 i 的项全部移到右边,得
2 * ( Fj - Fk )+ j2 - k2 + j - k < 2ij - 2ik
两边同时除去( 2j - 2k ),( 2j - 2k )<0
设 Gi = Fi + j2 +j, Vi = 2i ,则原不等式变为
仔细观察左边的式子,咦,好像有点熟悉是吧?
你没有看错,那就是传说中的圣斗士斜率!
所以当 Gj−GkVj−Vk > i 由 j 转移过来比由 k 转移过来更优。
反之,当 Gj−GkVj−Vk < i 由 k 转移过来比由 j 转移过来更优。
我们维护一个单调队列,队列里相邻的两个元素的斜率是单调递增。
当我们找到一个 i 时,发现队头元素 l ,以及对头后面的元素 p ,
发现 Gl−GpVl−Vp < i ,说明此时由 l 转移过来已经没有由 p 转移过来优了。
上面的式子也说明 Gl−GpVl−Vp < i +1< i +2<……< n ,也就说明从此以后,由 l 转移过来都不会由 p 转移过来优了,所以这时我们可以抛弃队头了,因为再也不会有任何卵用了。
完成 Fi 的赋值后,我们把 i 放到队尾。
这时我们发现由 i 转移过来可能比队内其他元素转移过来更加优,于是我们也可以从队尾往前更新。
设函数 kd(j,k) = Gj−GkVj−Vk ,队列最后一个元素的下标为 r ,该队列为 d ,
则如果 kd(dr−2,dr−1) > kd(dr−1,dr) ,则 dr−1 一定不是最优的,可以宣布报废,将其踢出队列,为什么呢?
从数的角度解释。
分类讨论
<1>如果 i > kd(dr−2,dr−1) > kd(dr−1,dr) ,则 dr−1 不比 dr 更优。
<2>如果 kd(dr−2,dr−1) > i > kd(dr−1,dr) ,则 dr−1 不比 dr−2 更优。
<3>如果 kd(dr−2,dr−1) > kd(dr−1,dr) > i ,则 dr−1 不比 dr−2 更优。
综上所述,无论如何, dr−1 都不是这三个元素中最优的,是个累赘,应该丢弃。
从形的角度解释。
kd(dr−2,dr−1) > kd(dr−1,dr) 的情况如下图
如下情况不满足队列的两两元素之间的斜率单调性,于是把 dr−1 去掉,这样新形成的最后两个元素的斜率会比原来的最后两个元素的斜率大,这样既可维护斜率单调性。
这就是斜率优化的全过程,对于每个元素,进队一次,出队一次,所以总时间复杂度为 O(n) ,可以拿100分。
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.