题目链接
http://www.lydsy.com/JudgeOnline/problem.php?id=1010
思路
很容易想到一个
O(n2)
的DP方程:
f[i]=min{f[j]+(i−j−1+sum[i]−sum[j]−L)2},j<i
其中 sum[x]=∑xi=1Ci
为了方便起见,我们让 sum[x]=∑xi=1(Ci+1),L=L+1
那么原方程变为了
f[i]=min{f[j]+(sum[i]−sum[j]−L)2},j<i
设 f[x]比f[y] 更优,则:
f[x]+sum[x]2−2sum[x](sum[i]−L)−f[y]−sum[y]2−2sum[y](sum[i]−L)<0
(f[x]+sum[x]2)−(f[y]+sum[y]2)sum[x]−sum[y]<2(sum[i]−L)
因此若队首的q[h+1]和q[h]连线斜率小于 2(sum[i]−L) ,即说明q[h+1]比q[h]优,弹出q[h]。而维护单调队列时,我们只需要维护一个斜率单调递增的上凸壳即可。。。
代码
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <algorithm>
#define MAXN 50100
using namespace std;
typedef long long int LL;
LL f[MAXN],sum[MAXN],q[MAXN]; //sum[i]=前i个玩具C之和
double getSlope(int x,int y)
{
return (double)((f[x]+sum[x]*sum[x])-(f[y]+sum[y]*sum[y]))/(sum[x]-sum[y]);
}
int main()
{
int n,L;
scanf("%d%d",&n,&L);
L++;
int h=1,t=1;
q[h]=0;
for(int i=1;i<=n;i++)
{
scanf("%lld",&sum[i]);
sum[i]+=sum[i-1];
sum[i]++;
}
//for(int i=1;i<=n;i++) sum[i]++;
for(int i=1;i<=n;i++)
{
while(h<t&&getSlope(q[h],q[h+1])<2*(sum[i]-L)) h++;
f[i]=f[q[h]]+(sum[i]-sum[q[h]]-L)*(sum[i]-sum[q[h]]-L);
while(h<t&&getSlope(q[t-1],q[t])>getSlope(q[t],i)) t--;
q[++t]=i;
}
printf("%lld\n",f[n]);
return 0;
}