【题意】:要打印一篇文章包含n个单词,每个单词打印需要一定的代价Ci。把连续k个单词打印在同一行需要花费(∑Ci)^2 + M,问打印完整篇文章最少代价是多少。
【题解】:一个分组问题,dp解决。
设dp[i]表示把前i个单词打印完的最少代价。
dp[i] = min(dp[j] + (sum[i] - sum[j]) ^ 2 + M),1 <= j <= i - 1.
朴素dp是O(n*n),而n<=500000,显然需要优化。
这题满足决策的单调性,所以只需要用一个单调队列维护一个下凸线,这样可以把转移降为O(1)。
【代码】:
1 #include "iostream"
2 #include "cstdio"
3 #include "cstring"
4 #include "algorithm"
5 #include "vector"
6 #include "queue"
7 #include "cmath"
8 #include "string"
9 #include "cctype"
10 #include "map"
11 using namespace std;
12 #define pb push_back
13 #define lc(x) (x << 1)
14 #define rc(x) (x << 1 | 1)
15 #define lowbit(x) (x & (-x))
16 #define ll long long
17 #define maxn 500050
18 int n, m;
19 int loc[maxn], head, tail;
20 ll sum[maxn], dp[maxn], cmp[maxn];
21 int main() {
22 while(~scanf("%d%d", &n, &m)) {
23 head = tail = 0, sum[0] = 0, dp[0] = 0;
24 loc[tail++] = 0;
25 for(int i = 1; i <= n; i++) {
26 scanf("%lld", &sum[i]);
27 sum[i] += sum[i-1];
28 }
29 for(int i = 1; i <= n; i++) {
30 while(head + 1 < tail && (cmp[loc[head+1]] - cmp[loc[head]]) <= 2 * sum[i] * (sum[loc[head+1]] - sum[loc[head]])) head++;
31 int j = loc[head];
32 dp[i] = dp[j] + (sum[i] - sum[j]) * (sum[i] - sum[j]) + m;
33 cmp[i] = dp[i] + sum[i] * sum[i];
34 while(head + 1 < tail && (cmp[loc[tail-1]] - cmp[loc[tail-2]]) * (sum[i] - sum[loc[tail-1]]) >= (cmp[i] - cmp[loc[tail-1]]) * (sum[loc[tail-1]] - sum[loc[tail-2]])) tail--;
35 loc[tail++] = i;
36 }
37 printf("%lld\n", dp[n]);
38 }
39 return 0;
40 }
41