洛谷 P4182
首先把所有线段按左端点升序,左端点相同按右端点升序排序。发现如果一个线段完全被另一个线段包含,那把它去掉也没有影响。于是我们先去掉这种线段,然后进行 DP 。设 fi,j 表示前 i 个区间删去 j 个,且钦定第 i 个区间不删除的最大覆盖点数。那么 fi,j=maxk<i(fk,j−(i−k−1)+ri−max(li,rk)) 。简单解释一下,就是钦定上一个选的是 k ,(k,i) 间的全都不选。因为 i−k−1≤K ,所以复杂度为 O(nK2)。
对于 k 比较小,即 rk<li 时的情况,fi,j=max(fk,j−(i−k−1)+ri−li)。如果我们能对于每一个 i−j−1 预处理出 fk,k−(i−j−1) 的最大值 p ,就可以 O(1) 转移了。
对于 k 比较大,即 rk>li 时的情况,fi,j=max(fk,j−(i−k−1)+ri−rk)。我们用单调队列维护 fk,j−(i−k−1)−rk 的最大值。当单调队列中的元素不满足 rk>li 时就弹出,弹出的时候顺便处理出 p。
总结一下,我们需要处理出 rk<li 时 fk,k−(i−j−1) 的最大值,再开 n 个单调队列,第 i−j 个单调队列维护 fi,j−ri 的最大值。这样复杂度就降为了 O(nK)。
Code:
#include <bits/stdc++.h>
using namespace std;
#define fi first
#define se second
typedef pair <int, int> pii;
const int N = 100005, M = 105;
int n, k;
pii a[N], b[N];
int f[N][M], p[N];
deque <int> q[N];
int main() {
scanf("%d%d", &n, &k);
for (int i = 1; i <= n; ++i) scanf("%d%d", &a[i].fi, &a[i].se);
if (k >= n) return printf("%d", 0), 0;
sort(a + 1, a + n + 1, [](pii x, pii y) {
return x.fi != y.fi ? x.fi < y.fi : x.se < y.se;
});
int mx = -1, tot = 0;
for (int i = 1; i <= n; ++i) {
if (a[i].se > mx) b[++tot] = a[i];
else --k;
mx = max(mx, a[i].se);
}
k = max(k, 0), b[++tot] = pii(1e9, 1e9);
for (int i = 1; i <= tot; ++i)
for (int j = 0; j <= min(i - 1, k); ++j) {
int t = i - j - 1;
while (q[t].size() && b[q[t].front()].se < b[i].fi) {
p[t] = max(p[t], f[q[t].front()][q[t].front() - t]);
q[t].pop_front();
}
f[i][j] = max(f[i][j], p[t] + b[i].se - b[i].fi);
if (q[t].size()) f[i][j] = max(f[i][j], f[q[t].front()][q[t].front() - t] + b[i].se - b[q[t].front()].se);
t = i - j;
while (q[t].size() && f[q[t].back()][q[t].back() - t] - b[q[t].back()].se <= f[i][j] - b[i].se) q[t].pop_back();
q[t].push_back(i);
}
printf("%d", f[tot][k]);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?