[JZOJ3432]服务器 ([上海交大OJ1061]小M的服务器)

Description

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

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

Solution

题意应该很清楚了

60%

直接 O(N2)dp
f[i] 表示已完成了 i ~N的所有服务器,并且 i 是直接复制的

转移直接枚举i+1kN

那么 f[i]=min(f[i],f[k]+Sum[ki1]+weight[i])
Sum[i] 表示1加到 i
为什么?
.
.
.
.
.
.
请自行脑补!!

90%(一个十分SB的优化)

可以发现,Sum的增长是越来越快的。

f[i+1] p 转移而来

那么因为在上一次p右边的服务器已经没有 p 优了。

Sum的增长速度随着距离增大而增大,那么显然 p 右边都没有必要搜了。

由于数据比较Water,可以过90%(我嘞个去!!

100%

这东西我第一眼看就像斜率优化dp

几百年前,一个熊孩子发现了 Sum 的公式

Sum[i]=(1+i)i2

然后我们代到式子里

f[i]=min(f[i],f[k]+(ki)(ki1)2+weight[i])

若选 j 比选k

f[j]+(ji)(ji1)2+weight[i]<f[k]+(ki)(ki1)2+weight[i]

整理

f[j]f[k]<k2j22i(jk)k+j2

继续整理~~

2f[j]2f[k]+kjk2+j2kj<2i

把左边设为 g(j,k)
那么只要满足这个式子, j 就比k

我们维护一个单调队列 d ,使g(d[i],d[i+1])(d[i+1],d[i+2])
g(d[1],d[2])2i

那队头必定是全队中最优的。

加入一个新的值时从后面往前查找到一个位置放入,删掉后面所有的元素。

但是,随着 i 的左移,队头的元素有可能开始不满足式子了,那就将其出队,直到当前队头满足为止。

于是在O(N)内解决

注意,这个熊孩子叫高斯

Code

#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<iostream>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fod(i,a,b) for(i=a;i>=b;i--)
using namespace std;
long long f[1000001],sum[1000001],pr[1000001],ans;
int i,j,n,d[1000001],head,tail;
double find(long long j,long long k)
{
    return(2*f[j]-2*f[k]+k-j-k*k+j*j)*1.0/(2*(j-k));
}
int main()
{
    cin>>n;
    fo(i,1,n) 
    {
        scanf("%d",&pr[i]);
        sum[i]=sum[i-1]+i;
        f[i]=1e+16;
    }
    f[n]=pr[n];
    ans=1e+16;
    head=1;
    tail=1;
    d[head]=n;
    fod(i,n-1,1)
    {       
        while (head!=tail&&find(d[head],d[head+1])>i) head++;
        f[i]=f[d[head]]+sum[d[head]-i-1]+pr[i];
        ans=min(ans,f[i]+sum[i-1]);     
        while(find(d[tail-1],d[tail])<find(d[tail],i)&&tail>head) tail--;
        d[++tail]=i; 
    }
    cout<<ans;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值