【上海交大OJ1061】小M的服务器
题目源自http://acm.sjtu.edu.cn/OnlineJudge/problem/1061
Description
我们需要将一个文件复制到n个服务器上,这些服务器的编号为S1, S2, …, Sn。
首先,我们可以选择一些服务器,直接把文件复制到它们中;将文件复制到服务器Si上,需要花费ci > 0的置放费用。对于没有直接被复制文件的服务器Si来说,它依次向后检查Si+1, Si+2, …直到找到一台服务器Sj:Sj中的文件是通过直接复制得到的,于是Si从Sj处间接复制得到该文件,这种复制方式的读取费用是j – i(注意j>i)。另外,Sn中的文件必须是通过直接复制得到的,因为它不可能间接的通过别的服务器进行复制。我们设计一种复制方案,即对每一台服务器确定它是通过直接还是间接的方式进行复制(Sn只能通过直接方式),最终使每一台服务器都得到文件,且总花费最小。
Data Constraint
60%的数据中,1 <= n <= 1 000
100%的数据中,1 <= n <= 1 000 000
80%的数据中, 1 <= ci <= 50
100%的数据中,1 <= ci <= 1 000 000 000
最终结果可能较大,请注意选择适当的数据类型进行计算。
Solution
对于60%的数据n比较小,我们可以设一个
Fi
,表示当前做到了
i
且第
转移显然:
Fi=min(Fj+(j−i−1)(j−i)2)+Ci
然后60%的数据就可以过了。
对于60%+的数据,我们观察上面的式子,如果 Fi 从 Fj 转移过来是最优的,那么 Fi−1 就没有必要从 Fj 以后的状态转移过来。
RP得分:70~90
对于100%的数据:
对于每个状态
Fi
,设它可以从
Fj
和
Fk(j>k)
转移过来,那么要满足
Fj<Fk
,
Fi
才可能从
Fj
转移过来。
那么对于 Fj<Fk ,我们有:
Fj+(j−i−1)(j−i)2+Ci<Fk+(k−i−1)(k−i)2+Ci
化简得:
2Fj+j2−2ij−j<2Fk+k2−2ik−k
把含有 i 项的都移到右边,得:
这个式子就满足单调性了。
我们设一个
g(j,k)
表示上方等式左边的结果,一开始把
n
加入队列,用
对于队头的处理,当我们发现
对于队尾的处理,我们要加入一个
i
,当
为什么呢?我们分类讨论一下:
当 i<g(dr−1,dr)<g(dr,i) 时, dr 比 dr−1 优,但不比 i 优。
当
当
g(dr−1,dr)<g(dr,i)<i
时,
dr
比
i
优,但不比
因此,当 g(dr−1,dr)<g(dr,i) 时, dr 都不是最优的,可以退队。
注意一点,
Fi
表示
i
必须选,所以答案不是
注意乘法。
Code
#include<iostream>
#include<cstdio>
#include<cstdlib>
#define fo(i,j,k) for(int i=j;i<=k;i++)
#define fd(i,j,k) for(int i=j;i>=k;i--)
#define N 1000010
#define ll long long
using namespace std;
ll f[N],a[N];
int d[N];
double g(int i,int j)
{
return (f[i]*2+i*1ll*i-i-f[j]*2-j*1ll*j+j)*0.5/(i-j);
}
int main()
{
int n;
cin>>n;
fo(i,1,n) scanf("%d",&a[i]);
f[n]=a[n];
int l=1,r=1;
d[1]=n;
fd(i,n-1,0)
{
while(l<r && g(d[l],d[l+1])>i) l++;
f[i]=f[d[l]]+(d[l]-i)*1ll*(d[l]-i-1)/2+a[i];
while(l<r && g(d[r-1],d[r])<g(d[r],i)) r--;
d[++r]=i;
}
cout<<f[0];
}