题面
解法
有一点技巧的区间dp
- 显然,对于每一个洗车店最后的价格,一定是某一个人想要的价格 c c 。如果不是且这个人的要求被满足了,那么一定可以将这个洗车店的价格正好改到,最后的总收益变大
- 那么,我们考虑这样一个dp:设 f[i][j][k] f [ i ] [ j ] [ k ] 表示区间 [i,j] [ i , j ] 的洗车店中,价格最小的为 k k 。其中,我们先对每一个人的价格离散化一下,是某一个价格对应的编号
- 考虑如何转移。我们不妨枚举中间哪一个是最小值,假设位置为 l l ,那么就可以得到转移方程:。其中, k′ k ′ 和 k′′ k ″ 分别为区间 [i,l−1] [ i , l − 1 ] 和 [l+1,j] [ l + 1 , j ] 的最小值,且满足 k≤k′,k′′ k ≤ k ′ , k ″ 。 cnt[k][l] c n t [ k ] [ l ] 为在 l l 这个位置有多少个人的价格不超过, c[k] c [ k ] 为离散化后编号为 k k 的实际价格是多少
- 然而,我们发现这个转移的复杂度其实是的,无法接受
- 观察这个式子,可以发现,因为 k′,k′′ k ′ , k ″ 一定不小于 k k ,对应的是一段后缀,那么我们可以令,那么转移的复杂度就变成了 O(n) O ( n )
- 因为要求出具体的方案,所以我们可以在转移的时候记录一下这个状态从哪里转移而来的即可
- 时间复杂度:
O(n3m)
O
(
n
3
m
)
【注意事项】 - 统计 cnt c n t 数组的时候需要注意,有点细节
代码
#include <bits/stdc++.h>
#define M 4010
#define N 55
using namespace std;
template <typename node> void chkmax(node &x, node y) {x = max(x, y);}
template <typename node> void chkmin(node &x, node y) {x = min(x, y);}
template <typename node> void read(node &x) {
x = 0; int f = 1; char c = getchar();
while (!isdigit(c)) {if (c == '-') f = -1; c = getchar();}
while (isdigit(c)) x = x * 10 + c - '0', c = getchar(); x *= f;
}
struct Node {
int l, r, v, id;
} a[M];
int ans[N], b[M], sum[M][N], f[N][N][M], g[N][N][M], h[N][N][M], pre[N][N][M];
void dfs(int l, int r, int k) {
if (l > r) return;
int x = pre[l][r][k]; ans[x] = b[k];
dfs(l, x - 1, h[l][x - 1][k]);
dfs(x + 1, r, h[x + 1][r][k]);
}
main() {
int n, m, len = 0; read(n), read(m);
for (int i = 1; i <= m; i++)
read(a[i].l), read(a[i].r), read(a[i].v), b[++len] = a[i].v;
sort(b + 1, b + len + 1); len = unique(b + 1, b + len + 1) - b - 1;
map <int, int> hh;
for (int i = 1; i <= len; i++) hh[b[i]] = i;
for (int i = 1; i <= m; i++) a[i].id = hh[a[i].v];
for (int ll = 1; ll <= n; ll++)
for (int i = 1; i <= n - ll + 1; i++) {
int j = i + ll - 1;
for (int k = 1; k <= len; k++)
for (int l = 1; l <= n; l++)
sum[k][l] = 0;
for (int k = 1; k <= m; k++)
if (i <= a[k].l && a[k].r <= j) sum[a[k].id][a[k].l]++, sum[a[k].id][a[k].r + 1]--;
for (int k = 1; k <= len; k++)
for (int l = 1; l <= n; l++)
sum[k][l] += sum[k][l - 1];
for (int k = len - 1; k; k--)
for (int l = 1; l <= n; l++)
sum[k][l] += sum[k + 1][l];
for (int k = 1; k <= len; k++)
for (int l = i; l <= j; l++) {
int tmp = g[i][l - 1][k] + g[l + 1][j][k] + sum[k][l] * b[k];
if (f[i][j][k] <= tmp) f[i][j][k] = tmp, pre[i][j][k] = l;
}
g[i][j][len] = f[i][j][len]; h[i][j][len] = len;
for (int k = len - 1; k; k--) {
g[i][j][k] = max(g[i][j][k + 1], f[i][j][k]);
if (g[i][j][k] == g[i][j][k + 1]) h[i][j][k] = h[i][j][k + 1];
else h[i][j][k] = k;
}
}
cout << g[1][n][1] << "\n";
int mxi;
for (int i = 1; i <= len; i++)
if (f[1][n][i] == g[1][n][1]) {mxi = i; break;}
dfs(1, n, mxi);
for (int i = 1; i <= n; i++) cout << ans[i] << ' '; cout << "\n";
return 0;
}