cf Educational Codeforces Round 78 D. Segment Tree

原题:
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,如果超过则出现两个线段包含的关系。直接跳出即可。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值