1. 程式人生 > 其它 >動態規劃做題記錄

動態規劃做題記錄

[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;
}