動態規劃做題記錄
阿新 • • 發佈:2022-03-27
[JSOI2011] 檸檬
複習一下斜率優化。
首先最優方案中每一段左右端點貝殼大小顯然相等,於是記 \(sum_i\) 為 \([1,i]\) 中所有大小為 \(s_i\) 的貝殼個數。
樸素方程為 \(f_i=\max\{f_j+(sum_i-sum_{j+1}+1)^2s_i\}\)
按照斜率優化套路展開得 \(f_i=\max\)\(\{\)\(f_j+sum_{j+1}^2s_i-2sum_{j+1}s_i-2sum_isum_{j+1}s_i\)\(\}+(sum_i+2sum_i+1)s_i\)
於是可得 \(f_i=\max\{y_j-k_ix_j\}+(sum_i+2sum_i+1)s_i\)
\(y_j=f_j+sum_{j+1}^2s_j\)
\(k_i=2sum_is_i\)
\(x_j=sum_{j+1}\)
對於每種大小開一個單調棧維護上凸包即可。
#include <cstdio> #include <vector> #define int long long #define gc (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 65536, stdin), p1 == p2) ? EOF : *p1 ++) #define X (Q[s[i + 1]].back()) #define Y (Q[s[i + 1]][Q[s[i + 1]].size() - 2]) char buf[65536], *p1, *p2; inline int max(const int x, const int y) {return x > y ? x : y;} inline int sqr(int x) {return x * x;} inline int read() { char ch; int x = 0; while ((ch = gc) < 48); do x = x * 10 + ch - 48; while ((ch = gc) >= 48); return x; } int s[100005], sum[100005], lst[10005], dp[100005]; std::vector<int> Q[10005]; inline int calc(int j, int i) {return dp[j] + sqr(sum[i] - sum[j + 1] + 1) * s[i];} inline double slope(int j, int i) { return (dp[i] + sqr(sum[i + 1]) * s[i + 1] - dp[j] - 1.0 * sqr(sum[j + 1]) * s[j + 1]) / (1.0 * sum[i + 1] - sum[j + 1]); } signed main() { int n = read(), ans = 0ll; for (int i = 1; i <= n; ++ i) sum[i] = sum[lst[s[i] = read()]] + 1, lst[s[i]] = i; for (int i = 1; i <= n; ++ i) { while (Q[s[i]].size() > 1 && calc(Q[s[i]].back(), i) <= calc(Q[s[i]][Q[s[i]].size() - 2], i)) Q[s[i]].pop_back(); if (Q[s[i]].size()) dp[i] = calc(Q[s[i]].back(), i); ans = max(ans, dp[i] = max(dp[i], calc(0, i))); while (Q[s[i + 1]].size() > 1 && slope(X, Y) <= slope(Y, i)) Q[s[i + 1]].pop_back(); Q[s[i + 1]].push_back(i); } printf("%lld", ans); return 0; }