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
~
转移直接枚举
那么
f[i]=min(f[i],f[k]+Sum[k−i−1]+weight[i])
Sum[i]
表示1加到
i
为什么?
.
.
.
.
.
.
请自行脑补!!
90%(一个十分SB的优化)
可以发现,
设 f[i+1] 由 p 转移而来
那么因为在上一次
由于数据比较Water,可以过90%(我嘞个去!!)
100%
这东西我第一眼看就像斜率优化
几百年前,一个熊孩子发现了 Sum 的公式
Sum[i]=(1+i)i2
然后我们代到式子里
f[i]=min(f[i],f[k]+(k−i)(k−i−1)2+weight[i])
若选
j
比选
则 f[j]+(j−i)(j−i−1)2+weight[i]<f[k]+(k−i)(k−i−1)2+weight[i]
整理
f[j]−f[k]<k2−j2−2i(j−k)−k+j2
继续整理~~
2f[j]−2f[k]+k−j−k2+j2k−j<2i
把左边设为
g(j,k)
那么只要满足这个式子,
j
就比
我们维护一个单调队列
d
,使
且
g(d[1],d[2])≤2i
那队头必定是全队中最优的。
加入一个新的值时从后面往前查找到一个位置放入,删掉后面所有的元素。
但是,随着 i 的左移,队头的元素有可能开始不满足式子了,那就将其出队,直到当前队头满足为止。
于是在
注意,这个熊孩子叫高斯
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;
}