正题
题目链接:https://www.luogu.com.cn/problem/P3195
题目大意
个物品,分成若干段,每一段的长度为 ,打包价格为
求最小价格和。
解题思路
设
表示
前面的都打包完了,有
然后
可以做到
,因为有平方,考虑斜率优化
定义
有
对于每一个 表示一个点 (后文中称之为决策点)
考虑如何使 最小,因为 是定值即让 最小,那么问题就变为了一条 的直线,要求经过某个决策点使得 最小。
那么显然,可能的点一定是一个下凸壳(相邻的点斜率单调上升),而因为 这个斜率也是单调上升的,我们可以知道答案就是第一个决策点满足与下一个决策点的斜率 。
那么我们维护一个单调队列即可。
时间复杂度
#include<cstdio>
#include<cstring>
#include<algorithm>
#define pow2(x) ((x)*(x))
using namespace std;
const int N=5e4+10;
struct node{
double x,y;
int num;
}q[N];
int n,head,tail,L;
double s[N],a[N],b[N],f[N];
double slope(node x,node y)
{return ((y.y-x.y)/(y.x-x.x));}
int main()
{
scanf("%d%d",&n,&L);
for(int i=1;i<=n;i++){
scanf("%lf",&s[i]);
s[i]+=s[i-1];
a[i]=s[i]+i;b[i]=s[i]+i+L+1;
}
b[0]=L+1;
head=tail=1;q[1]=(node){b[0],b[0]*b[0],0};
for(int i=1;i<=n;i++){
while(head<tail&&slope(q[head],q[head+1])<2*a[i])head++;
int p=q[head].num;f[i]=f[p]+pow2(a[i]-b[p]);
node w=(node){b[i],f[i]+b[i]*b[i],i};
while(head<tail&&slope(w,q[tail-1])<slope(q[tail-1],q[tail]))
tail--;
q[++tail]=w;
}
printf("%.0lf",f[n]);
}