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