前言
好像没什么好说的,快进入正题吧!
题目
8月P教授要去看奥运,但是他割舍不下自己的一大堆智力玩具。于是,他决定把所有玩具都运到北京去。P教授使用自己的物体维数压缩器ODZ(Object Dimension Zipper)来给玩具装箱。ODZ 可以将任意物品变成一维,再装到一种特殊的一维容器中。P教授有编号为1..N的N件玩具,第i件玩具经过ODZ处理后一维长度是Ci。为了方便整理,P教授要求在一个一维容器中的玩具编号是连续的。同时,如果一个一维容器中有多个玩具,那么相信两件玩具之间要加入1个单位长度的填充物。
形式地说,如果将第i到第j件玩具放在一个容器中,那容器的长度将为:
x=j-i+sigma(Ck) //i<=k<=j
制作容器的费用与容器长度有关。根据P教授的研究,如果容器长度为x,其制作费用为(x-L)^2,其中L是一个常量。P教授不关心容器的数目,他可以制造出任意长度的容器(甚至超过L),但他希望费用最小。
数据范围
1<=N<=50000,1<=L,Ci<=10^7。
题意
简单易懂,即分组,使得利益最大(即原题最小)。若是选择 l[p]到r[p] 则有k组,要求 Min(∑kp=1(r[p]−l[p]−1+∑r[p]i=l[p]Ci−L)2)
分析
明显的dp加优化,且与特别行动队一样方程都是与
i
有关的,所以照样上斜率优化。原方程为:
同样的,对于两个决策
j,k且j决策比k决策优又j>k
,则:
f[j]+(i−j−1−L+sum[i]−sum[j])2<f[k]+(i−k−1−L+sum[i]−sum[k])2
很快我们会发现这个方程里面的项太多了,怎么办?
继续我们又可以发现
i−j−1
的
i−j
是可以处理的,在前缀和里面——
sum[i]=sum[i]+i
便可以轻松处理了。那么还有一个1便可以插进l里面,即
l=l+1
,那么最后就可以减掉那个1了。再还有是我们的目的是把有
i
项的单独处理处理,其他可以一起处理。所以原方程就可以变成:
f[j]+sum[i]2+(sum[j]+L)2−2∗sum[i]∗(sum[j]+L)<f[k]+sum[i]2+(sum[k]+L)2−2∗sum[i]∗(sum[k]+L)
再移项:
f[j]−f[k]+(sum[j]+L)2−(sum[k]+L)2<2∗sum[i]∗(sum[j]−sum[k])
最后:
(f[j]−f[k]+(sum[j]+L)2−(sum[k]+L)2)(2∗(sum[j]−sum[k]))<sum[i]
这样便可以完美解决问题了!
#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<cstdlib>
#include<cstdio>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
const int N=50005;
int n,l,r;
long long g[N],sum[N],f[N],x,t;
double make(int x,int y){
return (f[x]-f[y]+(sum[x]+t)*(sum[x]+t)-(sum[y]+t)*(sum[y]+t))/(2*(sum[x]-sum[y]))*1.0;
}
int main(){
scanf("%d%lld",&n,&t);t+=1;
fo(i,1,n){scanf("%lld",&x);sum[i]=sum[i-1]+x;}
fo(i,1,n)sum[i]+=i;
l=1;r=1;
fo(i,1,n){
while ((l<r)&&(make(g[l+1],g[l])<sum[i])) l++;
f[i]=f[g[l]]+(sum[i]-sum[g[l]]-t)*(sum[i]-sum[g[l]]-t);
while ((l<r)&&(make(i,g[r])<make(g[r],g[r-1]))) r--;
g[++r]=i;
}
printf("%lld",f[n]);
}