题目链接:点击打开链接
题意:
给定n m
下面n个数
dp方程: dp[i] = dp[j] + sum[j+1, i] ^2 +m; ( j < i)
思路:
斜率优化
设 k < j,若从j转移过来更优则满足:
dp[j] + sum[j+1, i]^2 +m <= dp[k] + sum[k+1, i]^2 + m
整理得到
1、
((dp[j]+sum[j]^2) - (dp[k] + sum[k]^2) ) / (2*(sum[j]-sum[k])) <= sum[i]
注意sum[i]是不断增加的。所以一旦j点比k点更优,那么在i+1, i+2等也满足上面的不等式,即k点可以被放弃。
2、
当加入i点后,则 判断 i-1这点是否需要放弃。
双端队列维护一个斜率严格递增的图形。
这个图形的x轴是给出的n
y轴是斜率的值,即((dp[j]+sum[j]^2) - (dp[k] + sum[k]^2) ) / (2*(sum[j]-sum[k]))
#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <queue>
template <class T>
inline bool rd(T &ret) {
char c; int sgn;
if(c=getchar(),c==EOF) return 0;
while(c!='-'&&(c<'0'||c>'9')) c=getchar();
sgn=(c=='-')?-1:1;
ret=(c=='-')?0:(c-'0');
while(c=getchar(),c>='0'&&c<='9') ret=ret*10+(c-'0');
ret*=sgn;
return 1;
}
template <class T>
inline void pt(T x) {
if (x <0) {
putchar('-');
x = -x;
}
if(x>9) pt(x/10);
putchar(x%10+'0');
}
using namespace std;
const int N = 505000;
int n, m, a[N], sum[N], dp[N];
int q[N*2], tail, head;
int getDp(int i, int j){
return dp[j] + (sum[i]-sum[j])*(sum[i]-sum[j]) + m;
}
int getUp(int j, int k){
return dp[j]+sum[j]*sum[j] - (dp[k]+sum[k]*sum[k]);
}
int getDown(int j, int k){
return 2*(sum[j]-sum[k]);
}
int main(){
while(~scanf("%d %d", &n, &m)){
sum[0] = 0;
for(int i = 1; i <= n; i++)
{
rd(a[i]);
sum[i] = sum[i-1]+a[i];
}
dp[0] = 0;
head = tail = 0;
q[tail++] = 0;
for(int i = 1; i <= n; i++)
{
while(head+1 < tail && getUp(q[head+1], q[head])<=sum[i]*getDown(q[head+1],q[head]))
head++;
dp[i] = getDp(i, q[head]);
while(head+1 < tail && getUp(i, q[tail-1])*getDown(q[tail-1], q[tail-2]) <= getUp(q[tail-1], q[tail-2])*getDown(i, q[tail-1]))
tail--;
q[tail++] = i;
}
cout<<dp[n]<<endl;
}
return 0;
}