前言
策略肯定是锅了, 基础上需要对策略进行一些修改
喵了个咪的最终还是要针对考试
谢特
某吴姓同学的策略是非常适合我的, 在它的基础上, 我们考虑进行一些本土化
首先花 \(20 \textrm{min}\) 思考每道题, 也就是每道题严格 \(5 \textrm{ min}\)
首先按照能拿到的 \(\rm{subtask}\) 数量排为新的 \(\textrm{T2, T3, T4}\)
然后还是像之前一样化固定时间去冲, 注意不贪跟策略
你发现因为 \(20 + 35 \times 4 = 160 = \textrm{2h 40min}\), 剩了约 \(\textrm{1h}\), 因此这样大概率不可能完成
因此考虑先把简单的 \(\rm{subtask}\) 拿了放弃一道最难的或者最不会的题, 剩下的拼分
反正差不多这样吧, 哎哎哎, 太难了
思路
男人, 什么罐头我说, 曼巴出去!
题意
给定若干线段
要求你把这些线段分成 个集合, 每个集合的贡献是其中线段交的长度
求一种划分方式, 使得总贡献最大
去码头整点性质
凭感觉应该要先对线段排序之后做处理, 我们先按照左端点排一遍看有什么性质
考虑一种特殊情况, 即左右端点全部单增 \((\)我不会告诉你是因为我画草稿的时候忘了画包含情况不小心发现的\()\)
如下
这种情况下不难发现应该相邻分段, 考虑简单证明
证明
假设当前不是相邻分段的, 例如
那么我们考虑将其调整为相邻的, 不难发现贡献显然更大
继续整点性质, 不然还是没法做题
自然地, 我们考虑如果有包含关系应该怎么办, 接着画图
看似这是一种情况, 但是显然
这样更优
感觉也是有性质的, 只是说不太好找
看似这是一种情况, 但是显然
这样更优
所以初步猜测是要让上面几个单开, 下面的直接并到一起
这对吗?
应该很对啊
也就是把包含一条线段的分组, 然后这样子去做
那假如出现这种情况不就炸了吗
这是怎么分组的
发现按照被覆盖分组即可
也就是只保留第三跟第六根线段作为最终的结果
考虑具体怎么转移
这个真是太典了, 我们直接单调队列优化 + 滚动即可
实现
男人, 什么罐头我说, 曼巴出去!
代码
#include <algorithm>
#include <cstdio>
#include <vector>
#include <deque>const long long INF = 1e18;
const int MAXN = 5005;struct Segment { int l, r;bool operator<(const Segment& s) const {return l == s.l ? r > s.r : l < s.l;}
};int n, k;
Segment seg[MAXN];
long long global_res;// Case1: 存在空交集集合的情况
void handle_case1() {std::sort(seg + 1, seg + n + 1, [](auto& a, auto& b) {return a.r - a.l > b.r - b.l;});long long sum = 0;for (int i = 1; i < k; ++i)sum += seg[i].r - seg[i].l;int max_l = -1e9, min_r = 1e9;for (int i = k; i <= n; ++i) {max_l = std::max(max_l, seg[i].l);min_r = std::min(min_r, seg[i].r);}global_res = std::max(global_res, sum + std::max(0, min_r - max_l));
}// Case2: 所有集合交非空的情况
void handle_case2() {int m = 0;std::sort(seg + 1, seg + n + 1, [](const Segment& a, const Segment& b) {return a.l == b.l ? a.r > b.r : a.l < b.l;});std::vector<long long> le{0};for (int i = 1; i <= n; ) {while (m > 0 && seg[m].r >= seg[i].r) {le.push_back(seg[m].r - seg[m].l);m--;}m++;seg[m] = seg[i];i++;}std::sort(le.begin() + 1, le.end(), std::greater<long long>());while (le.size() <= k) le.push_back(0);for (size_t i = 1; i < le.size(); ++i)le[i] += le[i - 1];std::vector<long long> f(m + 1, -INF);f[0] = 0;for (int T = 1; T <= std::min(m, k); ++T) {std::vector<long long> g(m + 1, -INF);auto tmp = f;for (int i = 1; i <= m; ++i) {if (i - 1 >= 0 && i - 1 < tmp.size()) {tmp[i - 1] += seg[i].r;}}std::deque<int> q;for (int i = 1; i <= m; ++i) {while (!q.empty() && seg[q.front() + 1].r <= seg[i].l)q.pop_front();while (!q.empty() && tmp[q.back()] <= tmp[i - 1])q.pop_back();q.push_back(i - 1);g[i] = tmp[q.front()] - seg[i].l;}if (k - T >= 0 && k - T < le.size())global_res = std::max(global_res, g[m] + le[k - T]);f.swap(g);}
}int main() {scanf("%d%d", &n, &k);k = std::min(n, k);for (int i = 1; i <= n; ++i)scanf("%d%d", &seg[i].l, &seg[i].r);global_res = 0;handle_case1();handle_case2();printf("%lld\n", global_res);return 0;
}
总结
特有的最优解性质
比较 \(\rm{nb}\) 的观察到神秘贪心之后能够想到对其他情况做处理