【斜率优化】HDU 3507 Print Article
题意:给定一个长度为n的序列,和一个常数m,我们可以将序列分成随意段,每段的权值为sum(arr[i]) + C(l<=i<=r),求一种划分方法使得整个序列的权值最小
思路:
方程:dp[i] = min(dp[j] + (sum[i]-sum[j])^2 + m) (j < i);
则有:dp[i] = dp[j] + (sum[i]-sum[j])^2 + m = dp[j] + sum[i] * sum[i] + sum[j] * sum[j] - 2 * sum[i] * sum[j] + m;
设y = dp[j] + sum[j] * sum[j],x = sum[j],那么原式等于:dp[i] = y + 2 * sum[i] * x + m + sum[i] * sum[i]
代码:
1 #include <cstdio> 2 #include <cstring> 3 4 typedef long long ll; 5 6 const int MAX_N = 500007; 7 8 struct Point { 9 ll x, y, c; 10 Point () { 11 12 } 13 Point (ll _x, ll _y, ll _c) { 14 x = _x; 15 y = _y; 16 c = _c; 17 } 18 }; 19 20 int n, m; 21 int a[MAX_N]; 22 Point p[MAX_N]; 23 ll sum[MAX_N], dp[MAX_N]; 24 int que[MAX_N], head, tail; 25 26 int Check(int x, int y, int z) { 27 Point p0 = p[x], p1 = p[y], p2 = p[z]; 28 return (p0.x -p1.x) * (p0.y - p2.y) <= (p0.y - p1.y) * (p0.x - p2.x); 29 } 30 int NotBest(int x, int y, int k) { 31 Point p0 = p[x], p1 = p[y]; 32 return p0.y - k * p0.x > p1.y - k * p1.x; 33 } 34 35 ll Posi() { 36 head = tail = 0; 37 que[tail] = 0; 38 p[0].x = p[0].y = 0; 39 for (int i = 1; i <= n; ++i) { 40 p[i].x = sum[i - 1]; 41 p[i].y = dp[i-1] + sum[i - 1] * sum[i - 1]; 42 while (head <= tail - 1 && Check(que[tail - 1], que[tail], i)) tail--; 43 que[++tail] = i; 44 while (head + 1 <= tail && NotBest(que[head], que[head + 1], 2 * sum[i])) head++; 45 int k = que[head]; 46 dp[i] = p[k].y - 2 * sum[i] * p[k].x + sum[i] * sum[i] + m; 47 } 48 return dp[n]; 49 } 50 51 ll Nega() { 52 ll Min = 0; 53 for (int i = 1; i <= n; ++i) { 54 Min = -1; 55 for (int j = que[i - 1]; j < i; ++j) { 56 if (Min == -1 || dp[j] + (sum[i] - sum[j]) * (sum[i] - sum[j]) < Min) { 57 Min = dp[j] + (sum[i] - sum[j]) * (sum[i] - sum[j]); 58 que[i] = j; 59 } 60 } 61 dp[i] = Min + m; 62 } 63 return dp[n]; 64 } 65 66 67 int main() { 68 while (2 == scanf("%d%d", &n, &m)) { 69 for (int i = 1; i <= n; ++i) 70 scanf("%d",&a[i]), sum[i] = a[i] + sum[i - 1]; 71 ll ans = Nega(); 72 printf("%lld\n", ans); 73 } 74 return 0; 75 }