可以很容易得写出线性dp转移 d p [ i ] = m i n { ( s [ i ] − s [ j ] ) 2 + d p [ j ] + M ) } ( j ∈ [ 0 , i − 1 ] ) dp[i]=min\{ (s[i]-s[j])^2+dp[j]+M)\}(j\in[0,i-1]) dp[i]=min{(s[i]−s[j])2+dp[j]+M)}(j∈[0,i−1]),其中s[]为前缀和数组
那么直接把平方拆开,把只与j有关的放到斜率优化dp式子的左边。
可以得到式子 d p [ j ] + s [ j ] 2 = 2 s [ i ] s [ j ] − s [ i ] 2 + d p [ i ] − M dp[j]+s[j]^2=2s[i]s[j]-s[i]^2+dp[i]-M dp[j]+s[j]2=2s[i]s[j]−s[i]2+dp[i]−M
很显然这个式子 y = d p [ j ] + s [ j ] 2 , x = s [ j ] , k = 2 ∗ s [ i ] y=dp[j]+s[j]^2,x=s[j],k=2*s[i] y=dp[j]+s[j]2,x=s[j],k=2∗s[i],那么我们要让dp[i]尽可能小就是让与y轴交点尽可能小,而斜率是逐渐增大的,且(x,y)中的x和y都是逐渐增大的,所以我们维护一个下凸包(开口向上),用一个队列维护凸包上的点,头结点为当前斜率下的最小值,且队列构成一个斜率逐渐变大的凸包就行了,不断弹出头结点以获取最优值
#include<bits/stdc++.h>
#define pb push_back
using namespace std;
typedef long long ll;
const int maxl=5e5+10;
int n,m,cas,k,cnt,tot;ll ans;
ll a[maxl],sum[maxl],dp[maxl];
struct node
{
ll x,y;
}s[maxl];
bool in[maxl];
inline void prework()
{
for(int i=1;i<=n;i++)
scanf("%lld",&a[i]),sum[i]=sum[i-1]+a[i];
}
inline ll calc(ll k,int id)
{
return s[id].y-k*s[id].x;
}
inline bool cmpk(node a,node b,node c)
{
return 1.0*(b.y-a.y)*(c.x-b.x)>=1.0*(b.x-a.x)*(c.y-b.y);
}
inline void mainwork()
{
int hd=1,tl=0;ll k;
node d=node{0,0};
s[++tl]=d;
for(int i=1;i<=n;i++)
{
while(hd<tl && calc(2*sum[i],hd)>=calc(2*sum[i],hd+1))
hd++;
dp[i]=calc(2*sum[i],hd)+sum[i]*sum[i]+m;
d=node{sum[i],dp[i]+sum[i]*sum[i]};
while(hd<tl && cmpk(s[tl-1],s[tl],d))
--tl;
s[++tl]=d;
}
}
inline void print()
{
printf("%lld\n",dp[n]);
}
int main()
{
int t=1;
//scanf("%d",&t);
while(~scanf("%d%d",&n,&m))
{
prework();
mainwork();
print();
}
return 0;
}