2021集训新生赛4:区间覆盖PLUS

题目

#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;

const int N = 1e5 + 5;

struct line
{
    int l, r;
    int w;
    bool operator<(const line &l)
    {
        return w < l.w;
    }
} a[N];

struct node
{
    int l, r;
    int num;
    int add;
} tree[4 * N];

void build(int o, int l, int r)
{
    tree[o].l = l;
    tree[o].r = r;
    if (l == r)
        return;
    int mid = (r + l) / 2;
    build(2 * o, l, mid);
    build(2 * o + 1, mid + 1, r);
}

void pushdown(int o)
{
    if (tree[o].add)
    {
        tree[2 * o].num += tree[o].add;
        tree[2 * o].add += tree[o].add;
        tree[2 * o + 1].num += tree[o].add;
        tree[2 * o + 1].add += tree[o].add;
        tree[o].add = 0;
    }
}

void update(int o, int l, int r, int add)
{
    if (l <= tree[o].l && tree[o].r <= r)
    {
        tree[o].num += add;
        tree[o].add += add;
        return;
    }
    pushdown(o);
    int mid = (tree[o].l + tree[o].r) / 2;
    if (r > mid)
        update(2 * o + 1, l, r, add);
    if (l <= mid)
        update(2 * o, l, r, add);
    tree[o].num = min(tree[2 * o].num, tree[2 * o + 1].num);
}

int main()
{
    int n, m;
    cin >> n >> m;
    for (int i = 0; i < n; i++)
        cin >> a[i].l >> a[i].r >> a[i].w;
    sort(a, a + n);
    memset(tree, 0, sizeof(tree));
    build(1, 1, m - 1);//减一的原因是将其化为区间覆盖,即覆盖1~11的区间即可
    int l, r;
    l = r = 0;
    int ans = 1e6 + 5;
    while (1)
    {
        while (r < n && tree[1].num < 1)
        {
            update(1, a[r].l, a[r].r - 1, 1);
            r++;
        }
        if (!tree[1].num)
            break;
        ans = min(ans, a[r - 1].w - a[l].w);
        update(1, a[l].l, a[l].r - 1, -1);
        l++;
    }
    cout << ans << endl;
    return 0;
}

尺取法

首先判断出题目可以使用尺取法进行依次推进。
思考过程
想到尺取法便有几个需要解决的问题

  1. 推进过程中如何确定区间的覆盖程度以及是否完成了覆盖(由于使用尺取法必须对区间按照价值排序,故无法用一个最左值和最右值来判断区间的覆盖程度);

比较朴素的想法是用一个数组来记录每一个长度为1的区间是否被覆盖,然后每次推进时更新数组,并遍历数组观察是否完成了覆盖;这种做法的复杂度是O(nm),显然复杂度过高。
此时思考一个优化的方式,观察发现使用数组来维护区间覆盖的时间消耗主要是在覆盖程度的更新与完成覆盖的验证。于是想到用线段树来维护这个过程(1.每次更新覆盖程度时不用遍历每个点,找到对应线段即可;2.如果左子树与右子树都覆盖完成,显然父节点也完成覆盖,用这个来维护线段树。)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值