【斜率优化】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 } 
View Code

 

posted @ 2015-07-29 13:34  mithrilhan  阅读(160)  评论(0编辑  收藏  举报