洛谷 P3592

首先不难发现最终答案中只会出现 \(c_i\) 中的数,所以可以将 \(c_i\) 离散化。

\(f_{i,j,k}\) 表示区间 \([l,r]\),最小值不小于 \(k\) 的最大收益,\(cnt_{i,j}\) 表示区间穿过 \(i\),且区间的 \(c\ge j\) 的区间数量。

枚举最小的位置 \(p\),则有转移:

\[f_{l,r,k}=\max\limits_{l\lt p\lt r}\left\{f_{l,p-1,k}+f_{p+1,r,k}+cnt_{p,k}\times k\right\} \]

最后还要与 \(f_{l,r,k+1}\) 取个 \(\max\)

在 DP 过程中顺便记录决策点,然后 DFS 输出。

Code:

#include <bits/stdc++.h>
using namespace std;
const int N = 55, M = 4005;
int n, m;
int L[M], R[M], c[M], _[M], tot;
int f[N][N][M], val[N][N][M], pos[N][N][M];
int cnt[N][M];
int ans[N];

void print(int l, int r, int cur) {
	if (l > r) return;
	int mid = pos[l][r][cur = val[l][r][cur]];
	ans[mid] = _[cur];
	print(l, mid - 1, cur), print(mid + 1, r, cur);
}

void init(int l, int r) {
	for (int i = l; i <= r; ++i)
		for (int j = 1; j <= tot; ++j) cnt[i][j] = 0;
	for (int i = 1; i <= m; ++i)
		if (l <= L[i] && R[i] <= r)
			for (int j = L[i]; j <= R[i]; ++j)
				++cnt[j][c[i]];
	for (int i = l; i <= r; ++i)
		for (int j = tot - 1; j; --j) cnt[i][j] += cnt[i][j + 1];
}

void dp(int l, int r) {
	for (int k = tot; k; --k) {
		int mx = 0;
		for (int p = l; p <= r; ++p) {
			int tmp = f[l][p - 1][k] + f[p + 1][r][k] + cnt[p][k] * _[k];
			if (tmp >= mx) mx = tmp, pos[l][r][k] = p;
		}
		if (mx >= f[l][r][k + 1]) f[l][r][k] = mx, val[l][r][k] = k;
		else f[l][r][k] = f[l][r][k + 1], val[l][r][k] = val[l][r][k + 1];
	}
}

int main() {
	scanf("%d%d", &n, &m);
	for (int i = 1; i <= m; ++i) scanf("%d%d%d", &L[i], &R[i], &c[i]), _[++tot] = c[i];
	sort(_ + 1, _ + tot + 1), tot = unique(_ + 1, _ + tot + 1) - (_ + 1);
	for (int i = 1; i <= m; ++i) c[i] = lower_bound(_ + 1, _ + tot + 1, c[i]) - _;
	for (int len = 1; len <= n; ++len)
		for (int l = 1; l <= n; ++l) {
			int r = l + len - 1;
			if (r > n) break;
			init(l, r), dp(l, r);
		}
	print(1, n, 1);
	printf("%d\n", f[1][n][1]);
	for (int i = 1; i <= n; ++i) printf("%d ", ans[i]);
	return 0;
}
posted @ 2022-10-27 07:34  Kobe303  阅读(15)  评论(0编辑  收藏  举报