JZOJ.3432【GDOI2014模拟】服务器 解题报告

服务器

题目大意

我们需要将一份文件复制到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 + (ij)(ij1)2 ) (0 < j < i)
枚举上一个直接获得文件的服务器 j ,服务器j+1到服务器 i -1全部选择间接方式,他们间接复制的费用为(ij)(ij1)2,转移方程就出来了。

如果什么优化都不加,直接做这个状态转移方程,时间复杂度 On2 ,可以刷下60分。

至于100分,我们看到 (ij)(ij1)2 这个转移的费用是二次的,如果一次就好了,随便用个单调队列维护一下就可以了,
像这种二次的费用转移,要用到斜率优化。

假设由 j 转移到i比由 k 转移到i更优,( j <k)则有
Fj + (ij)(ij1)2 < Fk + (ik)(ik1)2

两边同乘2,得
2 * Fj+ (ij)(ij1) < 2 * Fk+ (ik)(ik1)

拆括号,得
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

2(FjFk)+j2k2+jk2j2k > i

Gi= Fi + j2 +j, Vi = 2i ,则原不等式变为

GjGkVjVk > i

仔细观察左边的式子,咦,好像有点熟悉是吧?

你没有看错,那就是传说中的圣斗士斜率!

所以当GjGkVjVk> i j转移过来比由 k 转移过来更优。
反之,当GjGkVjVk< i k转移过来比由 j 转移过来更优。

我们维护一个单调队列,队列里相邻的两个元素的斜率是单调递增。
当我们找到一个i时,发现队头元素 l ,以及对头后面的元素p
发现 GlGpVlVp < i ,说明此时由l转移过来已经没有由 p 转移过来优了。

上面的式子也说明GlGpVlVp< i +1<i+2<……< n ,也就说明从此以后,由l转移过来都不会由 p 转移过来优了,所以这时我们可以抛弃队头了,因为再也不会有任何卵用了。

完成Fi的赋值后,我们把 i 放到队尾。
这时我们发现由i转移过来可能比队内其他元素转移过来更加优,于是我们也可以从队尾往前更新。

设函数 kd(j,k) = GjGkVjVk ,队列最后一个元素的下标为 r ,该队列为d
则如果 kd(dr2,dr1) > kd(dr1,dr) ,则 dr1 一定不是最优的,可以宣布报废,将其踢出队列,为什么呢?

Reason One

从数的角度解释。
分类讨论
<1>如果 i >kd(dr2,dr1)> kd(dr1,dr) ,则 dr1 不比 dr 更优。
<2>如果 kd(dr2,dr1) > i >kd(dr1,dr),则 dr1 不比 dr2 更优。
<3>如果 kd(dr2,dr1) > kd(dr1,dr) > i ,则dr1不比 dr2 更优。
综上所述,无论如何, dr1 都不是这三个元素中最优的,是个累赘,应该丢弃。

Reason Two

从形的角度解释。
kd(dr2,dr1) > kd(dr1,dr) 的情况如下图

这里写图片描述

如下情况不满足队列的两两元素之间的斜率单调性,于是把 dr1 去掉,这样新形成的最后两个元素的斜率会比原来的最后两个元素的斜率大,这样既可维护斜率单调性。

这就是斜率优化的全过程,对于每个元素,进队一次,出队一次,所以总时间复杂度为 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.
  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值