P3592 [POI2015] MYJ 洗车店

P3592 [POI2015] MYJ 洗车店

点击查看代码
// 此题与人的区间[a,b]有关:区间DP;将[l,p-1],[p+1][r]的区间递归计算,经过p的区间
// f[l][r][k]表示l<=a<=b<=r的洗车店的价格>=k的最大收费
// f[l][r][k]=max{f[l][r][k+1], max{f[l][p-1][k]+f[p+1][r][k]+(l<=a<=p<=b<=r且c>=k的个数)*离散化前的k}}
#include <stdio.h>
#include <string.h>
#include <algorithm>
const int N = 55, M = 4005;
typedef long long LL;
int n, m, a[M], b[M], c[M];
int num[M], rnk[M], tot;
LL f[N][N][M];
int from[N][N][M], best[N][N][M]; // 记录来源p,最优的k
LL cnt[N][M]; // 省略l,r这两位: cnt[p][k]表示l<=a<=p<=b<=r且c>=k的个数
void print(int l, int r, int k) {
	if(l > r) return;
	print(l, from[l][r][k] - 1, best[l][r][k]);
	printf("%d ", num[best[l][r][k]]);
	print(from[l][r][k] + 1, r, best[l][r][k]);
}
int main() {
	scanf("%d%d", &n, &m);
	for(int i = 1; i <= m; i ++) scanf("%d%d%d", a + i, b + i, c + i), num[++ tot] = c[i];
	std::sort(num + 1, num + tot + 1), tot = std::unique(num + 1, num + tot + 1) - num - 1;
	for(int i = 1; i <= m; i ++) rnk[i] = std::lower_bound(num + 1, num + tot + 1, c[i]) - num;
	for(int len = 1; len <= n; len ++)
		for(int l = 1, r; (r = l + len - 1) <= n; l ++) {
			for(int p = l; p <= r; p ++)
				for(int k = 1; k <= tot; k ++) cnt[p][k] = 0;
			for(int i = 1; i <= m; i ++) // c==k的个数
				if(l <= a[i] && b[i] <= r)
					for(int p = a[i]; p <= b[i]; p ++) cnt[p][rnk[i]] ++;
			for(int p = l; p <= r; p ++) // 前缀和求出c>=k的个数
				for(int k = tot; k >= 0; k --) cnt[p][k] += cnt[p][k + 1];
			from[l][r][tot + 1] = l, best[l][r][tot + 1] = tot;
			for(int k = tot; k; k --) {
				f[l][r][k] = f[l][r][k + 1], from[l][r][k] = from[l][r][k + 1], best[l][r][k] = best[l][r][k + 1];
				for(int p = l; p <= r; p ++) {
					LL now = f[l][p - 1][k] + f[p + 1][r][k] + cnt[p][k] * num[k];
					if(now > f[l][r][k]) f[l][r][k] = now, from[l][r][k] = p, best[l][r][k] = k;
				}
			}
		}
	printf("%lld\n", f[1][n][1]), print(1, n, 1);
	return 0;
}
posted @ 2022-09-28 11:31  azzc  阅读(8)  评论(0编辑  收藏  举报