Address
Solution
非常有意思的题。
先把
c
i
c_i
ci 离散化。
定义状态:
f
[
l
]
[
r
]
[
i
]
f[l][r][i]
f[l][r][i] 表示区间
[
l
,
r
]
[l,r]
[l,r] 内的最小值为
i
i
i ,从满足
l
≤
a
≤
b
≤
r
l\le a\le b\le r
l≤a≤b≤r 的人中获得的最大收益。
边界
f
[
x
]
[
x
−
1
]
[
i
]
=
0
f[x][x-1][i]=0
f[x][x−1][i]=0 。
转移时枚举区间最小值所在的点
k
k
k 。
易得,这时候左端点在
[
l
,
k
]
[l,k]
[l,k] 内且右端点在
[
k
,
r
]
[k,r]
[k,r] 内的区间的最小值位置都是
k
k
k 。
统计出
c
n
t
cnt
cnt 表示满足
l
≤
a
≤
b
≤
r
l\le a\le b\le r
l≤a≤b≤r 且
a
≤
k
≤
b
a\le k\le b
a≤k≤b ,
c
≥
i
c\ge i
c≥i 的人有多少个。
转移就容易得出:
f
[
l
]
[
r
]
[
i
]
=
max
l
≤
k
≤
r
{
r
e
a
l
i
×
c
n
t
+
max
h
≥
i
f
[
l
]
[
k
−
1
]
[
[
h
]
+
max
h
≥
i
f
[
k
+
1
]
[
r
]
[
h
]
}
f[l][r][i]=\max_{l\le k\le r}\{real_i\times cnt+\max_{h\ge i}f[l][k-1][[h]+\max_{h\ge i}f[k+1][r][h]\}
f[l][r][i]=l≤k≤rmax{reali×cnt+h≥imaxf[l][k−1][[h]+h≥imaxf[k+1][r][h]}
其中
r
e
a
l
i
real_i
reali 表示离散化前
i
i
i 的实际值。
记录下
f
[
l
]
[
r
]
[
]
f[l][r][]
f[l][r][] 的后缀最大值,可以实现
O
(
n
3
m
)
O(n^3m)
O(n3m) 的优秀复杂度。
输出方案时只需要记录
f
[
l
]
[
r
]
[
i
]
f[l][r][i]
f[l][r][i] 从哪个
k
k
k 转移即可。
Code
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define For(i, a, b) for (i = a; i <= b; i++)
#define Rof(i, a, b) for (i = a; i >= b; i--)
inline int read()
{
int res = 0; bool bo = 0; char c;
while (((c = getchar()) < '0' || c > '9') && c != '-');
if (c == '-') bo = 1; else res = c - 48;
while ((c = getchar()) >= '0' && c <= '9')
res = (res << 3) + (res << 1) + (c - 48);
return bo ? ~res + 1 : res;
}
const int N = 55, M = 4005;
int n, m, tmpm, a[M], b[M], c[M], tmp[M], f[N][N][M], maxf[N][N][M],
cnt[M], mid[N][N][M], midf[N][N][M];
void addto(int l, int r, int mid)
{
int i;
For (i, 1, tmpm) cnt[i] = 0;
For (i, 1, m)
if (l <= a[i] && b[i] <= r && a[i] <= mid && mid <= b[i])
cnt[c[i]]++;
}
void output(int l, int r, int x)
{
int xmid = mid[l][r][x];
if (l < xmid) output(l, xmid - 1, midf[l][xmid - 1][x]);
printf("%d ", tmp[x]);
if (xmid < r) output(xmid + 1, r, midf[xmid + 1][r][x]);
}
int main()
{
int i, j, k, h;
n = read(); m = read();
For (i, 1, m) a[i] = read(), b[i] = read(), c[i] = tmp[i] = read();
std::sort(tmp + 1, tmp + m + 1);
tmpm = std::unique(tmp + 1, tmp + m + 1) - tmp - 1;
For (i, 1, m)
c[i] = std::lower_bound(tmp + 1, tmp + tmpm + 1, c[i]) - tmp;
Rof (i, n, 1) For (j, i, n)
{
For (k, i, j)
{
addto(i, j, k);
int sum = 0;
Rof (h, tmpm, 1)
{
sum += cnt[h];
int delta = maxf[i][k - 1][h] + maxf[k + 1][j][h] + tmp[h] * sum;
if (delta > f[i][j][h] || !f[i][j][h])
f[i][j][h] = delta, mid[i][j][h] = k;
}
}
Rof (h, tmpm, 1)
if (f[i][j][h] > maxf[i][j][h + 1] || !maxf[i][j][h + 1])
maxf[i][j][h] = f[i][j][h], midf[i][j][h] = h;
else maxf[i][j][h] = maxf[i][j][h + 1],
midf[i][j][h] = midf[i][j][h + 1];
}
std::cout << maxf[1][n][1] << std::endl;
output(1, n, midf[1][n][1]);
std::cout << std::endl;
return 0;
}