Educational Codeforces Round 112 E、Boring Segments

原题网址

https://codeforces.com/contest/1555/problem/E

题目大意

有n个区间。每个区间是[1,m]的子区间。从a可以一步走到b的充要条件是存在区间同时覆盖[a,b]。若n个区间中取出一些区间后,可以只通过被取出的区间从1走到m,则称这些被取出的区间组成的集合A是好的。每个区间有价值w,求一个好的集合A的所有元素中,最大价值与最小价值的差的最小值。

数据结构

线段树,支持以下两种操作:

  • 插入或删除区间
  • 查询当前区间集合是否为好的

若一个区间集是好的,则1.5,2.5,...,m-0.5都至少被一个区间覆盖,区间[a,b]可以覆盖a+0.5,...,b-0.5。所以我们可以将每个区间的右端点减一后再操作,线段树第a个元素表示a+0.5被多少个区间覆盖。这样只要查询线段树中[1,m-1]最小值是否为0,不为0即好的。

由于m较大,必须使用懒修改方法,每个节点增加lazy值(区别于真正的val值)。需要注意:

  • 访问某节点时,先进行push操作(将本节点lazy值转给val值,如果还有孩子,则lazy值传递给孩子,清空本节点lazy值),不管被查区间是多少(若l>r也要push,因为只要能递归到,就表示该点实际上表示了一个区间,需要把lazy值转给val值)。
  • 修改时,如果某个节点表示的区间全都要修改,只修改该节点lazy值,不递归。改完后必须进行push操作,把lazy值转成val并传递给孩子。
  • 递归返回时,将两个孩子的val组合成本节点的val。

解题思路

本题为最优化问题。显然尽可能选价值差较小的区间。
先将所有区间按价值排序。
用双指针维护一个范围,左指针为给定价值最小值时,右指针为最小的价值最大值。
枚举所有最小值,根据最小值指针和线段树查询结果移动最大值指针并更新最后答案(用最大值-最小值更新ans)。

我的代码

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
constexpr int maxn = 300000;
constexpr int maxm = 2000000;
constexpr int inf = 0x7fffffff;

struct seg {
    int l, r, w;
};

bool operator<(const seg& a, const seg& b) { return a.w < b.w; }

int n, m;
int tree[maxm * 4 + 2];
int lazy[maxm * 4 + 2];
seg segs[maxn + 4];

inline int lc(int i) { return i << 1; }
inline int rc(int i) { return (i << 1) + 1; }

inline void pushdown(int i) {
    lazy[lc(i)] += lazy[i], lazy[rc(i)] += lazy[i];
    tree[lc(i)] += lazy[i], tree[rc(i)] += lazy[i];
    lazy[i] = 0;
}

int query(int s, int e, int l, int r, int i) {
    if (s <= l && r <= e) return tree[i];
    pushdown(i);
    auto m = (l + r) >> 1;
    int sum = inf;
    if (s <= m) sum = min(query(s, e, l, m + 1, lc(i)), sum);
    if (e >= m + 1) sum = min(query(s, e, m + 1, r, rc(i)), sum);
    return sum;
}

void update(int s, int e, int l, int r, int i, int val) {
    if (s <= l && r <= e) {
        lazy[i] += val, tree[i] += val;
        return;
    }
    pushdown(i);
    auto m = (l + r) >> 1;
    if (s <= m) update(s, e, l, m, lc(i), val);
    if (e >= m + 1) update(s, e, m + 1, r, rc(i), val);
    tree[i] = min(tree[lc(i)], tree[rc(i)]);
}

int main() {
    scanf("%d%d", &n, &m);

    for (int i = 1; i <= n; ++i) {
        scanf("%d%d%d", &segs[i].l, &segs[i].r, &segs[i].w);
    }
    sort(segs + 1, segs + n + 1);

    int p2 = 1;
    int ans = inf;
    for (int p1 = 1; p1 <= n; ++p1) {
        while (p2 < n + 1 && query(1, m - 1, 1, m - 1, 1) == 0) {
            update(segs[p2].l, segs[p2].r - 1, 1, m - 1, 1, 1);
            ++p2;
        }
        if (query(1, m - 1, 1, m - 1, 1) == 0) break;
        ans = min(ans, segs[p2 - 1].w - segs[p1].w);
        update(segs[p1].l, segs[p1].r - 1, 1, m - 1, 1, -1);
    }
    printf("%d\n", ans);
    return 0;
}
posted @ 2021-08-10 19:11  capacito  阅读(47)  评论(0编辑  收藏  举报