【GDOI2014模拟】JZOJ2020年8月14日提高组 服务器

【GDOI2014模拟】JZOJ2020年8月14日提高组 服务器

题目

Time and Memory Limits

在这里插入图片描述

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

在这里插入图片描述

题解

题意

给出 n n n个点
每个点可以选择两种操作
一种是直接复制,费用为 a i a_i ai
一种是间接复制,费用为 i i i后面第一个直接复制的 j j j j − i j-i ji
n n n号点必须直接复制
问最小代价

分析

考虑 D P DP DP
f [ i ] f[i] f[i]表示 i i i点直接复制和间接复制的总和
那么转移
f [ i ] = m i n { f [ j ] + j ∗ ( j − i ) − ( j − i ) ( j + i − 1 ) 2 } f[i]=min\{f[j]+j*(j-i)-\dfrac{(j-i)(j+i-1)}{2}\} f[i]=min{f[j]+j(ji)2(ji)(j+i1)}
转移 n 2 n^2 n2,过不了
思考优化
发现可以斜率优化
优化
f [ j ] − f [ k ] + j 2 − k 2 + k − j 2 j − k < i ( j > k ) \dfrac{f[j]-f[k]+\dfrac{j^2-k^2+k-j}{2}}{j-k}<i(j>k) jkf[j]f[k]+2j2k2+kj<i(j>k)
单调队列维护

Code

#include<bits/stdc++.h>
using namespace std;
long long n,l,r,ans,f[1000005],q[1000005],a[1000005];
long long read()
{
	long long res=0;char ch;
	ch=getchar();
	while (ch<'0'||ch>'9') ch=getchar();
	while (ch>='0'&&ch<='9')
	{
		res=(res<<1)+(res<<3)+(ch-'0');
		ch=getchar();
	}
	return res;
}
long long get(long long l,long long r)
{
	return r*(r-l)-(r+l-1)*(r-l)/2;
}
double slope(long long x,long long y)
{
	return (f[x]-f[y]+(x*x-y*y+y-x)/2)*1.0/(x-y);
}
int main()
{
	n=read();
	for (long long i=1;i<=n;i++)
		a[i]=read();
	f[n]=a[n];
	l=1;
	r=1;
	q[1]=n;
	for (long long i=n-1;i;i--)
	{
		while (l<r&&slope(q[l],q[l+1])>=i) l++;
		f[i]=a[i]+f[q[l]]+get(i+1,q[l]);
		while (l<r&&slope(q[r-1],q[r])<slope(q[r],i)) r--;
		q[++r]=i;
	}
	ans=99999999999999;
	for (long long i=1;i<=n;i++)
		ans=min(ans,f[i]+get(1,i));
	printf("%lld\n",ans);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值