这个题目又是一个典型的dp斜率优化的题目。题意是给你n个数,你需要做的是把这个n个数分为连续的若干段,每段的权值为这段数字的和的平方加上M。求最小的总权值。
我们可以根据题意写出朴素版的dp状态转移方程。然后就可以推导出使用优先队列来维护最优值。总共包括对首和队尾的两个操作。
很简单,很朴素的题目,直接上代码吧。。。。就是范围定义小了,然后Wa了n^n^n^n^……发。 深坑啊。。。
#include <iostream>
#include <cstdio>
#include <cstring>
#define maxn 500500
#define ll long long
using namespace std;
ll f[maxn],a[maxn],sum[maxn];
ll n,m,xx,yy,xx1,yy1;
ll q[maxn],head,tail;
ll dy(ll j1,ll j2) { return f[j2]+sum[j2]*sum[j2]-f[j1]-sum[j1]*sum[j1]; }
ll dx(ll j1,ll j2) { return 2*(sum[j2]-sum[j1]); }
int main()
{
while (scanf("%I64d%I64d",&n,&m)!=EOF)
{
for (ll i=1; i<=n; i++) scanf("%I64d",&a[i]),sum[i]=sum[i-1]+a[i];
head=tail=1,q[1]=0;
for (ll i=1; i<=n; i++)
{
while (tail-head>0)
{
xx=dx(q[head],q[head+1]);
yy=dy(q[head],q[head+1]);
if (sum[i]*xx>=yy) head++;
else break;
}
f[i]=f[q[head]]+(sum[i]-sum[q[head]])*(sum[i]-sum[q[head]])+m;
while (tail-head>0)
{
yy=dy(q[tail-1],q[tail]);
xx=dx(q[tail-1],q[tail]);
yy1=dy(q[tail],i);
xx1=dx(q[tail],i);
if (yy*xx1>=yy1*xx) tail--;
else break;
}
q[++tail]=i;
}
printf("%I64d
",f[n]);
}
return 0;
}