题意:直接看题意即可
题解:这是一道斜率dp题,首先我们设dp[i]为到第i位的最小花费,然后可以得出这个状态转移式:
dp[i]=min(dp[j]+(sum(i)-sum(j)+i-j-l-1)^2)(0<=j<i)
可以设M=l+1,s[i]=sum[i]+i,s[j]=sum[j]+j
然后就可以将方程写为:
dp[i]=min(dp[j]+(s[i]-s[j]-M)^2)
进行展开之后:
dp[i]=min(dp[j]+s[i]^2-2*s[i]*(s[j]+M)+(s[j]+M)^2)
之后可以将有关i的移出min之外:
dp[i]=min(dp[j]-2*s[i]*s[j]+s[j]^2+2*M*s[j])+s[i]^2-2*M*s[i]+M^2
然后设y=dp[j]+s[j]^2+2*M*s[j],x=s[j],k=2*s[i]
可以设G=y-k*x,导一下,y=k*x+G,然后就是单调队列维护一下下凸包进行dp得出答案
附上代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=5e4+50;
ll n,m,l,c[maxn],dp[maxn],sum[maxn],s[maxn];
struct point{
ll x,y;
point(ll _x=0,ll _y=0):x(_x),y(_y){}
};
point q[maxn];
int head,tail;
ll multi(point o,point a,point b)
{
return (a.x-o.x)*(b.y-o.y)-(a.y-o.y)*(b.x-o.x);
}
int main()
{
scanf("%lld%lld",&n,&l);
m=l+1;
for(int i=1;i<=n;i++){
scanf("%lld",&c[i]);
sum[i]=sum[i-1]+c[i];
}
for(int i=1;i<=n;i++){
s[i]=sum[i]+i;
}
head=tail=0;
dp[0]=0;
q[tail++]=point(0,0);
for(int i=1;i<=n;i++){
while(head+1<tail&&q[head].y-2*s[i]*q[head].x>q[head+1].y-2*s[i]*q[head+1].x){
head++;
}
dp[i]=q[head].y-2*s[i]*q[head].x+s[i]*s[i]-2*m*s[i]+m*m;
point p=point(s[i],dp[i]+s[i]*s[i]+2*m*s[i]);
while(head+1<tail&&multi(q[tail-2],q[tail-1],p)<0){
tail--;
}
q[tail++]=p;
}
printf("%lld\n",dp[n]);
return 0;
}