传送门:https://www.luogu.org/problem/P3195
显而易见:f[i]=min{ f[j]+( i-j-1+ sum[i]-sum[j]-m)^2 } (f[i]表示前i件的最小花费)
化简 f[i]=f[j]+( (i+sum[i]) - (j+sum[j]+m+1) )^2
另 A(x)=x+sum[x] B(x)=A(x)+m+1
上式化简 f[i]=f[j]+ A(i)^2 - 2*A(i)*B(j) + B(j)^2
f[j] + B(j)^2 =f[i] + 2*A(i)*B(j) -A(i)^2
可见 是以 B(j) 为横坐标 f[j]+ B(j)^2 为纵坐标 2*A(i)为斜率
转移就很舒服了 YY一下就知道了!在队列转移时我用了 {l=1,r=0,l<=r}然后挂了,用的{l=r=0,l<r}就过了?
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<cstdio> #define R register typedef long long ll; using namespace std; ll n,m,c[51000],sum[51000],q[51000],l=0,r=0,f[51000]; ll a(ll x){ return sum[x]+x; } ll b(ll x){ return a(x)+m+1; } ll Y(ll x){ return f[x]+b(x)*b(x); } ll X(ll x){ return b(x); } double slope(ll x,ll y){ return (double)(Y(y)-Y(x))/(X(y)-X(x)); } int main (){ scanf("%lld%lld",&n,&m); for(R int i=1;i<=n;i++) scanf("%lld",&c[i]),sum[i]=sum[i-1]+c[i]; for(R int i=1;i<=n;i++){ while(l<r&&slope(q[l+1],q[l])<=a(i)<<1) l++; f[i]=(f[q[l]]+(a(i)-b(q[l]))*(a(i)-b(q[l]))); while(l<r&&slope(i,q[r])<=slope(q[r],q[r-1])) r--; q[++r]=i; } printf("%lld",f[n]); return 0; }