题目链接:点击打开链接
题意:
给定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; }
原文地址:http://blog.csdn.net/qq574857122/article/details/41481233