Educational Codeforces Round 78 (Rated for Div. 2) D. Segment Tree

题目链接

题意:

n 个点, 每个点有一个区间,任意两个区间的值都不一样, 值从 1 - 2*n, 如果两个区间有交,那么代表这两个点有边,问这n个点是不是一棵树

思路:

刚看完题就会有一个简单的思路:
那就是先排序, 然后判断两个点的区间是不是交, 如果是交的话那么就用 并查集 并起来。
简单的思路就是这样, 但是实现起来的时候,时间复杂度会很大,
所以我们要想一种优化的方法,优化时间复杂度。

于每个数都不一样, 有 2*n 数,
我们首先按照左区间从小到大排序,然后把我们遍历过的点的右区间的值放到map 中,
对于当前点 i , 之前遍历过的点的左区间的值肯定比 i 的左区间的值小,
然后我们根据 i 的左区间的值在 map 中 lower_bound 找第一个右区间的值大于 i 的左区间的值
然后这个map 中的值还要小于 i 的右区间的值。
这样时间复杂度就降了下来,

反思:

我一开始是两边for 循环, 第一遍 for 循环就是循环当前点 i,
然后第二遍的for 循环从 i + 1 开始, 这个时候后面的点的 左区间都大于当前点的左区间,然后我们找到 后面点的左区间小于当前点的右区间,后面点的右区间大于当前点的右区间。
但是这样做的话, 中间很多的for 循环都是没有意义的, 因为他的右区间不大于当前点的右区间。

我交了一次, 果不其然他 T 了, 然后我就想办法优化, 但是太菜了,没有想出来。
正好做,然后想优化是真的不太可能。

但是我们可以倒着想, 把之前用过的点存下来。
我之前的想法是把当前点之后的点存下来, 这样的话要考虑的东西太多了。
所以以后考虑的时候, 当前点的后面不行了, 我们向前面考虑。

#include<bits/stdc++.h>
using namespace std;
const int N = 5e5+100;
void dbg() {cout << endl;}
template<typename T, typename... A> void dbg(T a, A... x) {cout << a << ' '; dbg(x...);}
#define logs(x...) {cout << #x << " -> "; dbg(x);}
struct node{
    int l,r,id;
    bool operator < (const node &A) const{
        return l < A.l;
    }
}g[N];
int m,n;
int f[N];
map<int,int>mp;
int find(int k){
    if (f[k] == k) return k;
    return f[k] = find(f[k]);
}
int main(){
    int x,y;
    scanf("%d",&n);
    for (int i = 0; i < n; ++i){
        scanf("%d%d",&g[i].l, &g[i].r);
        g[i].id = i;
        f[i] = i;
    }
    sort(g,g+n);
    for (int i = 0; i < n; ++i){
        auto it = mp.lower_bound(g[i].l);
        while(it != mp.end() && (it -> first) < g[i].r){
            x = find(i);
            y = find(it -> second); 
            if (x != y){
                f[x] = y;
            } else{
                puts("NO");
                return 0;
            }
            it++;
        }
        mp[g[i].r] = i;
    }
    for (int i = 0; i < n; ++i)
        find(i); 
    for (int i = 0; i < n; ++i)
        if (f[i] != f[0]) {
            puts("NO");
            return 0;
        }
    puts("YES");
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值