jzoj. 3432. 【GDOI2014模拟】服务器

Description

我们需要将一个文件复制到n个服务器上,这些服务器的编号为S1, S2, …, Sn。

首先,我们可以选择一些服务器,直接把文件复制到它们中;将文件复制到服务器Si上,需要花费ci > 0的置放费用。对于没有直接被复制文件的服务器Si来说,它依次向后检查Si+1, Si+2, …直到找到一台服务器Sj:Sj中的文件是通过直接复制得到的,于是Si从Sj处间接复制得到该文件,这种复制方式的读取费用是j – i(注意j>i)。另外,Sn中的文件必须是通过直接复制得到的,因为它不可能间接的通过别的服务器进行复制。我们设计一种复制方案,即对每一台服务器确定它是通过直接还是间接的方式进行复制(Sn只能通过直接方式),最终使每一台服务器都得到文件,且总花费最小。

Input

输入文件的第一行有一个整数n,表示服务器的数目。输入文件的第二行有n个整数,顺数第i个表示ci:在Si上直接复制文件的费用。

Output

输出文件中只包含一个整数,即最少需要花费的费用。

Sample Input

10

2 3 1 5 4 5 6 3 1 2

Sample Output

18

Data Constraint

60%的数据中,1 <= n <= 1 000

100%的数据中,1 <= n <= 1 000 000

80%的数据中, 1 <= ci <= 50

100%的数据中,1 <= ci <= 1 000 000 000

最终结果可能较大,请注意选择适当的数据类型进行计算。

Hint

这里写图片描述

分析:显然dp。设状态f[i]为选了i的最优解。f[i]=min(f[j]+a[i]+(j-i) * (j-i-1) * 2),从j转移过来,显然中间的都不选,可以得60分,再进行小优化,可以得80分。最后就是要斜率优化了。我们有两个状态f[j],f[k] (i < j,k) 我们判断这两个状态那个转移过来更优,显然有:
f[j]+a[i]+(j-i) * (j-i-1) * 2 < f[k]+a[i]+(k-i) * (k-i-1) * 2

化简得:
((f[j]-f[k]+(j^2-k^2+k-j)/2)/(j-k) > i
{我先在笔记本上推了一次,讲题时还是犯了些小错误}

我们设g(j,k)=((f[j]-f[k]+(j^2-k^2+k-j)/2)/(j-k) {就是左边的那一堆}
我们对于这样一组状态,若
g(1,2) < i g(2,3) < i ……g(t-1,t) < i {t为队列末尾}

1比2优,2比3优……

若遇到这种状态,
g(1,2) > i,则2这种状态显然比1优,1就可以去掉。

以为其单调性,有
那么在单调队列中必须满足
i > g(1,2) > g(2,3) > g(3,4)……
那么只要维护这个就行了,每个i做完后将i入队。若出现了一个新的数据破坏了单调性,如:
g(2,3) > g(3,4) < g(4,5) ,显然4这个状态是无效的。

代码:

var
 f,a:array [0..100001] of int64;
 list:array [0..100001] of int64;
 n,i,j,h,t:longint;

function min(x,y:int64):int64;
 begin
  if x<y then exit(x)
         else exit(y);
 end;

function check(j,k:int64):extended;
 begin
  exit((f[j]-f[k]+(j*j-k*k-j+k)/2)/(j-k));
 end;

begin
 readln(n);
 fillchar(f,sizeof(f),0);
 for i:=1 to n do
  read(a[i]);
 f[n]:=a[n];
 h:=1;
 t:=1;
 list[t]:=n;
 for i:=n-1 downto 0 do
  begin
   if t>h then
     while check(list[h],list[h+1])>=i do
     begin
      inc(h);
      if h=t then break;
     end;
   j:=list[h];
   f[i]:=f[j]+a[i]+(j-i)*(j-i-1) div 2;
   if t>h then
     while check(list[t-1],list[t])<check(list[t],i) do
      begin
       dec(t);
       if t=h then break;
      end;
   inc(t);
   list[t]:=i;
  end;
 writeln(f[0]);
end.
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值