题目链接
题意:
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;
}