1 条题解
-
0
题解
把所有区间按左端点升序、若左端点相同则按右端点升序排序。显然,若某个区间的右端点不超过前面出现过的最大右端点,那么不论保留与否都不会给答案带来贡献,可以直接删除,并把需要解雇的名额
K
相应减掉(这些牛等价于“免费删除”)。处理完之后得到的区间集合满足右端点严格递增。若删完被支配的区间之后
K ≤ 0
,说明我们已经解雇足够多人,此时只需顺序扫描一遍求所有区间的并长即可。下面假设仍需再解雇
K
头牛。设排序后的区间为p[1…n]
,其中p[i-1].r < p[i].r
。我们令dp[i][j]
表示在前i
个区间中恰好解雇了j
头牛,且第i
个区间被保留下来时,能够覆盖的最大时间。为了转移,我们需要选择上一个保留的区间t < i
:中间的i-t-1
个区间都被解雇,覆盖增量为p[i].r - max(p[i].l, p[t].r),
而若两区间重叠,则只有第一次出现覆盖的区间可以把左端作为贡献。由于右端点串严格递增,
p[t].r
是一个随t
单调递增的函数,上述转移可以看成线性函数的最大值,因此我们可以沿着j-i
为常数的对角线遍历dp
,并用单调队列维护在滑动窗口中的候选t
。这样就能把原本的O(nK^2)
转移降低到O(nK)
。最终答案等价于在最后一个区间之前保留
n-K
个区间时的最大覆盖值——代码中对所有可能的末尾位置i
取dp[i][i+K-n]
的最大值即可。#include<bits/stdc++.h> using namespace std; const int N = 1e5; const int M = 1e2; const int mod = 1e9 + 7; const int INF = 0x3f3f3f3f; int n, m, ans; int f[N + 5][M + 5]; int que[M + 5]; struct Seg { int l, r; bool friend operator < (Seg a, Seg b) { if (a.l == b.l) { return a.r < b.r; } return a.l < b.l; } }p[N + 5]; int main() { ios::sync_with_stdio(0); cin.tie(0);cout.tie(0); cin >> n >> m; for (int i = 1; i <= n; i++) { cin >> p[i].l >> p[i].r; } sort(p + 1, p + 1 + n); int tmpr = -INF, cnt = 0; for (int i = 1; i <= n; i++) { if (p[i].r > tmpr) { p[++cnt] = p[i]; tmpr = p[i].r; } } m -= (n - cnt); n = cnt; if (m <= 0) { tmpr = -INF; for (int i = 1; i <= n; i++) { ans += p[i].r - max(p[i].l, tmpr); tmpr = p[i].r; } cout << ans; return 0; } memset(f, -0x3f, sizeof f); for (int i = 0; i <= m; i++) { f[i][i] = 0; } for (int d = -1; d >= -n; d--) { int x = d + 1, sum = -INF; int l = 1, r = 0; for (int j = 0; j <= min(m, n + d); j++) { int i = j - d; while (l <= r && p[que[l]].r <= p[i].l) { sum = max(sum, f[que[l]][x + que[l]]); l++; } if (p[i - 1].r <= p[i].l) { sum = max(sum, f[i - 1][x + i - 1]); } else { while (l <= r && f[que[r]][x + que[r]] - p[que[r]].r <= f[i - 1][x + i - 1] - p[i - 1].r) { r--; } que[++r] = i - 1; } f[i][j] = sum - p[i].l + p[i].r; if (l <= r) { f[i][j] = max(f[i][j], f[que[l]][x + que[l]] - p[que[l]].r + p[i].r); } } } for (int i = n - m; i <= n; i++) { ans = max(ans, f[i][i + m - n]); } cout << ans; return 0; }
- 1
信息
- ID
- 3260
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 8
- 标签
- 递交数
- 2
- 已通过
- 1
- 上传者