原题:
D. Segment Tree
time limit per test
2 seconds
memory limit per test
256 megabytes
input
standard input
output
standard output
As the name of the task implies, you are asked to do some work with segments and trees.
Recall that a tree is a connected undirected graph such that there is exactly one simple path between every pair of its vertices.
You are given n segments [l1,r1],[l2,r2],…,[ln,rn], li<ri for every i. It is guaranteed that all segments’ endpoints are integers, and all endpoints are unique — there is no pair of segments such that they start in the same point, end in the same point or one starts in the same point the other one ends.
Let’s generate a graph with n vertices from these segments. Vertices v and u are connected by an edge if and only if segments [lv,rv] and [lu,ru] intersect and neither of it lies fully inside the other one.
For example, pairs ([1,3],[2,4]) and ([5,10],[3,7]) will induce the edges but pairs ([1,2],[3,4]) and ([5,7],[3,10]) will not.
Determine if the resulting graph is a tree or not.
Input
The first line contains a single integer n (1≤n≤5⋅10^5) — the number of segments.
The i-th of the next n lines contain the description of the i-th segment — two integers li and ri (1≤li<ri≤2n).
It is guaranteed that all segments borders are pairwise distinct.
Output
Print “YES” if the resulting graph is a tree and “NO” otherwise.
Examples
Input
6
9 12
2 11
1 3
6 10
5 7
4 8
Output
YES
Input
5
1 3
2 4
5 9
6 8
7 10
Output
NO
Input
5
5 8
3 6
2 9
7 10
1 4
Output
NO
Note
The graph corresponding to the first example:
中文:
给你一堆线段,相交的线段可以堪称两个节点连接一条边。问你最后连接的这些边的图,能不能变成一棵树。
#include<iostream>
#include<algorithm>
#include<string>
#include<vector>
#include<string.h>
#include<unordered_map>
#include<map>
#include<set>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
const int maxn = 500010;
int n;
vector<pii> end_ind;
vector<pii> seg;
vector<int> G[maxn];
bool used[maxn];
void dfs(int v)
{
used[v] = 1;
for (int i = 0; i < G[v].size(); i++)
{
if (!used[G[v][i]])
dfs(G[v][i]);
}
}
int main()
{
ios::sync_with_stdio(false);
while (cin >> n)
{
int a, b;
for (int i = 0; i < n; i++)
G[i].clear();
memset(used, 0, sizeof(used));
seg.clear();
end_ind.clear();
for (int i = 0; i < n; i++)
{
cin >> a >> b;
seg.push_back(make_pair(a, b));
end_ind.push_back(make_pair(a, i));
end_ind.push_back(make_pair(b, i));
}
sort(end_ind.begin(), end_ind.end());
set<pii> cur;
int cnt = 0;
for (int i = 0; i < end_ind.size(); i++)
{
if (cnt >= n)
break;
if (cur.find(end_ind[i]) != cur.end())
cur.erase(end_ind[i]);
else
{
int cur_r = seg[end_ind[i].second].second;
for (auto it = cur.begin(); it != cur.end(); it++)
{
if (it->first > cur_r)
break;
G[end_ind[i].second].push_back(it->second);
G[it->second].push_back(end_ind[i].second);
cnt++;
if (cnt >= n)
break;
}
cur.insert(make_pair(cur_r, end_ind[i].second));
}
}
dfs(0);
if (cnt == n - 1 && count(used, used + n, 1) == n)
cout << "YES" << endl;
else
cout << "NO" << endl;
}
return 0;
}
思路:
这题思路很简单直白,只要能再nlogn的复杂度把图建出来就完活了,判断一个图是否是一棵树,只要判断是不是连通且没有环就行了。
官方题解里面给出了一个利用set找出所有线段之间相交的简单方法,思维比较巧妙。
1.首先将所有线段的左右端点标记上线段的下标号,以pair的形式存储。(point,ind)
2.对所有的pair,从小到大排序。
3.维护一个set存储当前线段cur,当遇到一个线段的起点cur_left时,cur加入集合,遇到当前线段的终点cur_right时,从集合中删除cur。由于所有的点都是时从小到大排序后遍历的,这样依次找到的点,如果不是cur_right,那么该点一定与cur相交,如果该点时cur_right,则将cur从set中删除。
4.判断当前点所对应线段的的右端点是否超过cur_right,如果超过则出现两个线段包含的关系。直接跳出即可。