“景驰科技杯”2018年华南理工大学程序设计竞赛 G. Youhane as "Bang Riot"(斜率DP)

题目链接:https://www.nowcoder.com/acm/contest/94/G

题意:中文题目,见链接

题解:设 sum[i] 为 a[i] 的前缀和,可得公式

dp[i] = min( dp[j] + ( sum[i] - sum[j] -  b[i] ) ^ 2 )

          = min( dp[j] + sum[j] ^ 2 + 2 * ( sum[i] - b[i] ) * ( sum[i] - sum[j] ) + sum[i] - b[i] ) ^ 2 

设 y = dp[j] + sum[j] ^ 2,k = sum[i] - b[i],x = sum[i] - sum[j],b = sum[i] - b[i] ) ^ 2

有 y = 2 * k * x + b

因为 k 的正负不确定,但 y 和 x 符合单调性,故可以二分找出第一个比当前点斜率大的点来维护下凸性(注意最好手写二分,使用lower_bound可能会出错)

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 #define ll long long
 4 #define ull unsigned long long
 5 #define mst(a,b) memset((a),(b),sizeof(a))
 6 #define mp(a,b) make_pair(a,b)
 7 #define pi acos(-1)
 8 #define pii pair<int,int>
 9 #define pb push_back
10 const int INF = 0x3f3f3f3f;
11 const double eps = 1e-6;
12 const int MAXN = 1e6 + 10;
13 const int MAXM = 1e3 + 10;
14 const ll mod = 100000073;
15  
16 int n,tail;
17 int q[MAXN];
18 ll a[MAXN],b[MAXN],sum[MAXN],dp[MAXN];
19 double k[MAXN];
20  
21 ll sqr(ll x) {
22     return x * x;
23 }
24  
25 ll getup(int j,int k) {
26     return dp[j] + sqr(sum[j]) - (dp[k] + sqr(sum[k]));
27 }
28  
29 ll getdown(int j,int k) {
30     return 2ll * (sum[j] - sum[k]);
31 }
32  
33 int findd(double x) {
34     int l = 0, r = tail - 1, ans = 0;
35     while(l <= r) {
36         int mid = (l + r) >> 1;
37         ll up = dp[q[mid]] + sqr(sum[q[mid]]) - (dp[q[mid - 1]] + sqr(sum[q[mid - 1]]));
38         ll down = 2.0 * (sum[q[mid]] - sum[q[mid - 1]]);
39         if(up <= down * x) l = mid + 1, ans = mid;
40         else r = mid - 1;
41     }
42     return q[ans];
43 }
44  
45 int main() {
46 #ifdef local
47     freopen("data.txt", "r", stdin);
48 #endif
49     sum[0] = dp[0] = 0;
50     while(~scanf("%d",&n)) {
51         for(int i = 1; i <= n; i++) {
52             scanf("%lld",&a[i]);
53             sum[i] = sum[i - 1] + a[i];
54         }
55         for(int i = 1; i <= n; i++) scanf("%lld",&b[i]);
56         tail = 0;
57         q[tail++] = 0;
58         for(int i = 1; i <= n; i++) {
59             int id = findd(1.0 * (sum[i] - b[i]));
60             dp[i] = dp[id] + sqr(sum[i] - sum[id] - b[i]);
61             while(1 < tail && getup(q[tail - 1],q[tail - 2]) * getdown(i,q[tail - 1]) >= getup(i,q[tail - 1]) * getdown(q[tail - 1],q[tail - 2]))
62                 tail--;
63             if(getdown(i,q[tail - 1]) == 0) k[tail] = 1e18;
64             else k[tail] = 1.0 * getup(i,q[tail - 1]) / getdown(i,q[tail - 1]);
65             q[tail++] = i;
66         }
67         printf("%lld
",dp[n]);
68     }
69     return 0;
70 }
原文地址:https://www.cnblogs.com/scaulok/p/9770065.html