[H并查集] lc1851. 包含每个查询的最小区间(并查集+离散化+周赛239_4)

9 篇文章 0 订阅
3 篇文章 0 订阅

1. 题目来源

链接:1851. 包含每个查询的最小区间

前导题:[并查集] aw3115. 疯狂的馒头(经典并查集+离线询问+离线算法+模板)

2. 题目解析

本次周赛的 2 3 4 题质量都相当高。

本次第四题方法很多,线段树、树状数组、multiset、并查集等都可以做。

采用并查集解法,一定先去掌握前导题 前导题:[并查集] aw3115. 疯狂的馒头(经典并查集+离线询问+离线算法+模板),这是一个非常经典的并查集模型,必须掌握。

注意:

摘自,加了一点自己的理解:

作者:daydayUppp
链接:https://leetcode-cn.com/problems/minimum-interval-to-include-each-query/solution/chi-san-hua-chi-xian-si-wei-bing-cha-ji-w2osk/
来源:力扣(LeetCode)

  • 离散化。 本题数据范围很大,数据量很少,需要离散化。n 的范围是 1e7 的数量级,所以对每一段区间去更新每一个点一定会超时。所以换一个角度,只对每一段区间的端点和要查询的点考虑则数量级就到了 1e5 的数量级 即使 nlogn 的时间复杂度也可以通过,所以把 区间端点每个查询 的点,加入一个数组,并排序这样就把最多 1e7 个点 离散成 1e5 个点。但其实这个解法是可以处理 10^7 的数据的,力扣太慢了,10^7 会超时。例比 疯狂的馒头
  • 离线思维。 因为把每一个区间都事先给出,所以可以事先利用区间信息来处理数据,实现优化因为由题目可以看出区间长度越短优先度越高,即: 先更新优先度高的区间,再更新优先度低的,这样不会重复更新,保证最优答案。
  • 并查集。 思路和疯狂的馒头一模一样,没啥好说的。

离散化用的少…掌握这个思维和处理方式。


  • 时间复杂度 O ( n l o g n ) O(nlogn) O(nlogn),主要在排序的时间上,并查集在此没有按秩合并,所以理论时间复杂度是 O ( n l o g n ) O(nlogn) O(nlogn) 的,但实际时间复杂度可以媲美 O ( 1 ) O(1) O(1)
  • 空间复杂度 O ( n ) O(n) O(n)

并查集+离散化:

class Solution {
public:
    vector<int> alls, p, w;     // 离散化、并查集、颜色

    // 在此并查集没有按秩合并,仅有路径压缩,时间复杂度为 O(logn),但是一般情况下为 O(1)
    // 得到 x 右边第一个未被染色的点
    int find(int x) {             
        if (p[x] != x) p[x] = find(p[x]);
        return p[x];
    }

    // 离散化得到下标
    int get(int x) {
        return lower_bound(alls.begin(), alls.end(), x) - alls.begin();
    }

    vector<int> minInterval(vector<vector<int>>& intervals, vector<int>& queries) {
        // 值域 1e7,数量 1e5,故可以离散化区间端点和查询区间长度
        for (auto s : intervals) alls.push_back(s[0]), alls.push_back(s[1]);
        for (auto x : queries) alls.push_back(x);
        sort(alls.begin(), alls.end());
        alls.erase(unique(alls.begin(), alls.end()), alls.end());   

        int n = alls.size();
        // 多初始化一个元素,因为最后一个元素被染色之后会指向下一个元素,故 n+1 号元素也会被用到     
        p.resize(n + 1), w.resize(n + 1, -1);  
        for (int i = 0; i < n + 1; i ++ ) p[i] = i;   // 初始化并查集

        // 按区间长度从小到大排序,离线算法:将所有询问按照特定的顺序重排序,再依次进行处理
        sort(intervals.begin(), intervals.end(), [](vector<int> &a, vector<int> &b) {
            return a[1] - a[0] < b[1] - b[0];
        });

        // 区间长度越小优先级越高,按区间长度从小到大将区间每个点覆盖一遍并染上颜色,保证了区间长度最短,最优覆盖
        for (auto it : intervals) {
            int l = get(it[0]), r = get(it[1]), len = it[1] - it[0] + 1;    // 得到离散化后的左右端点,得到区间长度
            while (find(l) <= r) {      // l 的祖先存的是下一个未被染色的点,若仍在该区间内,则对其进行染色
                l = find(l);            // 该点的祖先存的是下一个没有染色的点,即更新这个左端点
                p[l] = l + 1;  // p[l]存l的右边第一个未被染色的点。此时l将被染成 len,让p[l]=l+1,达到将该点删除的目的
                w[l] = len;    
            }
        }

        // 离线求结果,和 1847 最近的房间,同周双周赛的第4题一样,同为离线算法
        vector<int> res;
        for (auto x : queries) res.push_back(w[get(x)]);

        return res;
    }
};
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Ypuyu

如果帮助到你,可以请作者喝水~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值