dp
~
f[i]=min{f[j]+(sum[i]−sum[j]+i−j−1−L)2}
这显然是
O(n2)
啃腚会
T
写个小小的斜率优化
设
f[j]+(g[i]−g[j]−1−L)2−f[k]−(g[i]−g[k]−1−L)2<0
时,我们显然会选择
j
点
变形一下得
再变
f[j]−f[k]g[j]−g[k]+g[j]+g[k]<2(g[i]−L−1)
可以发现右边那个式子是不下降的,左边就是我们要的斜率
所以应该维护斜率不下降,否则就是前面的不够优,可以被后面的白吃
出队列时同样判断前两个点斜率是否比 2(g[i]−L−1) 小,小的话就是后面更好,前面的出队列
搞清楚后 1A 也是挺容易的
代码
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define gg getchar()
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
inline ll read(){
ll x=0,f=1;char ch=gg;
for(;ch<'0'||ch>'9';ch=gg)if(ch=='-')f=-1;
for(;ch>='0'&&ch<='9';ch=gg)x=x*10+ch-'0';
return x*f;
}
inline void out(ll x){
int a[25],t=0;
if(x<0)putchar('-'),x=-x;
for(;x;x/=10)a[++t]=x%10;
for(int i=t;i;--i)putchar('0'+a[i]);
if(t==0)putchar('0');
putchar('\n');
}
int tou,wei;
ll f[50050],dui[50050],sum[50050],n,L,le[50050];
inline ll sqr(ll x){return x*x;}
inline ll g(ll x){return sum[x]+x;}
inline ll xie(ll j,ll k){return (f[j]-f[k])/(g(j)-g(k))+g(k)+g(j);} //斜率
void push(int x){
for(;tou<wei;--wei){
if(xie(x,dui[wei])>xie(dui[wei],dui[wei-1]))break;
}
dui[++wei]=x;
}
void pop(int m){
for(;tou<wei;++tou){
int k=dui[tou],j=dui[tou+1];
if(xie(j,k)>m)break;
}
}
int main(){
n=read();L=read();
for(int i=1;i<=n;++i)le[i]=read(),sum[i]=sum[i-1]+le[i];
f[0]=0;
f[1]=sqr(le[1]-L);
dui[tou=0]=0;
dui[wei=1]=1;
for(int i=2;i<=n;++i){
pop(2*(g(i)-L-1));
f[i]=f[dui[tou]]+sqr(g(i)-g(dui[tou])-L-1);
push(i);
}
out(f[n]);
return 0;
}