专栏文章
题解:P10784 【MX-J1-T4】『FLA - III』Wrestle
P10784题解参与者 1已保存评论 0
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 0 条
- 当前快照
- 1 份
- 快照标识符
- @miniskxt
- 此快照首次捕获于
- 2025/12/02 03:06 3 个月前
- 此快照最后确认于
- 2025/12/02 03:06 3 个月前
我竟然自己一个人把这道题切出来了(花了两天,只算零碎时间是 1 个小时)。算是考前给自己的信心。写完了还是有些感触的,故写篇题解祭。
先读题目。
因为是处理线段关系,所以不管三七二十一,先把蓝色线段按照 为关键字排序,红色线段按照 为关键字排序。
每个点至多被一个红色线段和一个蓝色线段覆盖。说明蓝色线段之间不会有交集,红色线段亦然。故 且 。
定义 表示第 个红色线段为最左的和第 个蓝色线段有交集的线段, 表示第 个红色线段为最右的和第 个蓝色线段有交集的线段。因为 的单调性且同色线段互不相交,所以可以通过二分求解 和 。
选取线段是有代价的,即为有交的红色线段的权值。也是有收益的,即为交集长度。定义 为选取第 个蓝色线段的收益, 为选取第 个蓝色线段的代价。根据题目,。
而第 个蓝色线段选取的收益就比较复杂——要处理边际。如果有 和 ,则需要特殊处理。中间的皆为连续的,加上 。
这两个求和,用前缀和优化。
再将 按照 排序。
而每个红色线段至多被一个蓝色线段覆盖。所以选取的蓝色线段的 不可以相交。
问题转化成了:有一些线段,代价为 ,收益为 ,起始点为 ,终止点为 。求选取一些线段的最大收益,使得总代价不超过 ,且线段两两不相交(这里没有什么蓝色红色线段了)。
等等,这不就是 01 背包吗!
但因为不可以相交,所以方程为:
而这样的时间复杂度是 ,只能拿 50 分。考虑优化。
表示 ,这是可以预处理的。二分最小的 使得 ,在处理到 的时候即可预处理 。此时:
时间复杂度 。
因此易得代码:
CPP#include <bits/stdc++.h>
#define rty printf("Yes\n");
#define RTY printf("YES\n");
#define rtn printf("No\n");
#define RTN printf("NO\n");
#define rep(v,b,e) for(int v=b;v<=e;v++)
#define repq(v,b,e) for(int v=b;v<e;v++)
#define rrep(v,e,b) for(int v=b;v>=e;v--)
#define rrepq(v,e,b) for(int v=b;v>e;v--)
#define stg string
#define vct vector
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
void solve() {
}
const int N = 2E5 + 5;
struct RedSegment {
int l, r;
ll w;
}reds[N];
struct BlueSegment {
int l, r;
ll v, w;
}blues[N];
ll reds_pref[N], reds_len[N], dp[5005][5005], cnt, dp_pre[5005][5005], blue_l[5005];
ll cover[N];
bool cmp(RedSegment a, RedSegment b) {
return a.l < b.l;
}
bool check(int red, pair<int, int> blue) {
return max(blue.first, reds[red].l) <= min(blue.second, reds[red].r);
}
bool cmp2(BlueSegment a, BlueSegment b) {
return a.l < b.l;
}
main() {
// int t; cin >> t; while (t--) solve();
int n, m, k;
cin >> n >> m >> k;
rep(i, 1, n)
cin >> reds[i].l >> reds[i].r >> reds[i].w;
sort(reds + 1, reds + n + 1, cmp);
rep(i, 1, n) {
reds_pref[i] = reds_pref[i - 1] + reds[i].w;
// 权值的前缀和
reds_len[i] = reds_len[i - 1] + reds[i].r - reds[i].l + 1;
// 收益的前缀和
}
rep(i, 1, m) {
int lef, rig;
cin >> lef >> rig;
int left_red = 1, r = n;
int l = 1;
while (l < r) {
int mid = (l + r + 1) / 2;
if (reds[mid].l > lef)
r = mid - 1;
else
l = mid;
}
left_red = max(1, l);
if (!check(left_red, {lef, rig})) {
left_red++;
}
// 二分 si
int right_red = 1; l = left_red, r = n;
while (l < r) {
int mid = (l + r) / 2;
if (reds[mid].r < rig)
l = mid + 1;
else
r = mid;
}
right_red = min(n, r);
if (!check(right_red, {lef, rig}))
right_red--;
// 二分 ti
ll v = 0, w = 0;
// 计算收益与代价
if (right_red - left_red >= 0) {
w = reds_pref[right_red] - reds_pref[left_red - 1];
// 代价易得
int tmpl = left_red, tmpr = right_red;
if (reds[left_red].l < lef) {
v = min(rig, reds[left_red].r) - max(reds[left_red].l, lef) + 1;
tmpl++;
}
if (tmpr >= tmpl && reds[right_red].r > rig) {
v += min(rig, reds[right_red].r) - max(lef, reds[right_red].l) + 1;
tmpr--;
}
// 收益需要处理边际
if (tmpr - tmpl >= 0)
v += reds_len[tmpr] - reds_len[tmpl - 1];
blues[++cnt] = {left_red, right_red, v, w};
}
}
sort(blues + 1, blues + cnt + 1, cmp2);
rep(i, 1, cnt) {
blue_l[i] = blues[i].l;
// 因为我二分很烂 qwq,单开一个数组可以用 upper_bound(
}
ll ans = 0;
rep(i, 1, cnt) {
int ending = upper_bound(blue_l + 1, blue_l + cnt + 1, blues[i].r) - blue_l;
rep(j, 0, k) {
dp_pre[i][j] = max(dp_pre[i - 1][j], dp_pre[i][j]);
if (j < blues[i].w) continue;
dp[i][j] = dp_pre[i][j - blues[i].w] + blues[i].v;
dp_pre[ending][j] = max(dp_pre[ending][j], dp[i][j]);
ans = max(ans, dp[i][j]);
// dp_pre 为 F 数组
// 动规优化
}
}
cout << ans;
return 0;
}
相关推荐
评论
共 0 条评论,欢迎与作者交流。
正在加载评论...