题意:Hyunuk会举行一场会议今年,会议由n门演讲组成,Hyunuk有两个候选场馆a和b,每门演讲在不同的场馆都有不同的时间段,分别是[sa, ea] [sb, eb],对于任意两个演讲,如果存在一对演讲,在a场馆时间段相交,在b场馆时间段不相交,或者在b场馆时间段相交,在a场馆时间段不相交,那么意味着这场会议是场馆敏感的,输出YES,否则输出NO。

分析:我们枚举每门演讲i,然后二分查找出和它相交的演讲(在场馆A),然后再判断这两门相交的演讲在场馆B,是否也相交,如果在B不相交,说明是场馆敏感的,同时,我们还需要在B场馆再做一遍这个操作,因为,我们在A场馆二分出来的是和演讲i相交的演讲,然后再去B场馆判断这两门演讲相不相交,因此,我们还需要再做一遍操作,在场馆B中二分,在A场馆中寻找两对相不相交。$$即在A场地冲突是否一定在B场地冲突的情况和在B场地冲突是否一定在A场地冲突的情况都要判断一遍$$

我们使用stl的lower_bound函数去二分演讲i的相交演讲,返回起点大于等于末尾的演讲,假设返回的位置是pos,pos如果是大于等于i + 1,意味着pos和i之间存在着一个或多个相交区间,这些区间都是相交的,然后我们再去查询这些区间的末端最小值和顶端最大值,即ebmin < sbi || sbmax > ebi,即是否至少存在一个在a中和i相交的区间在b中和i不相交,如果存在,就是敏感的,这个,我们可以采用线段树进行优化,返回给定区间的最小值和最大值。

//代码借鉴一个博主的

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;
const int N = 100005;

struct Node
{
	int sa;
	int ea;
	int sb;
	int eb;
	Node(){}
	Node(int _sa, int _ea, int _sb, int _eb):sa(_sa), ea(_ea), sb(_sb), eb(_eb){}
	bool operator<(const Node& rhs)const
	{
		if (sa != rhs.sa)
			return sa < rhs.sa;
		else
			return ea < rhs.ea;
	}
}node[N];

//线段树
struct tr
{
	//该结点代表的区间
	int l;
	int r;
	//该区间涵盖的演讲的最小值
	int mi;
	//该区间涵盖的演讲的最大值
	int mx;
}tr[N * 4];

void pushup(int u)
{
	tr[u].mi = min(tr[u << 1].mi, tr[u << 1 | 1].mi);
	tr[u].mx = max(tr[u << 1].mx, tr[u << 1 | 1].mx);
}

void build(int u, int l, int r)
{
	//叶节点
	if (l == r)
	{
		tr[u] = { l, r, node[l].eb, node[l].sb };
		return;
	}
	int mid = (l + r) >> 1;
	build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
	pushup(u);
}

//l,r是结点的范围
int querymin(int u, int l, int r, int al, int ar)
{
	if (l == al && r == ar)
	{
		return tr[u].mi;
	}
	int mid = (l + r) >> 1;
	if (ar <= mid) return querymin(u << 1, l, mid, al, ar);
	else if (al > mid) return querymin(u << 1 | 1, mid + 1, r, al, ar);
	else return min(querymin(u << 1, l, mid, al, mid), querymin(u << 1 | 1, mid + 1, r, mid + 1, ar));
}

int querymax(int u, int l, int r, int al, int ar)
{
	if (l == al && r == ar)
	{
		return tr[u].mx;
	}
	int mid = (l + r) >> 1;
	if (ar <= mid) return querymax(u << 1, l, mid, al, ar);
	else if (al > mid) return querymax(u << 1 | 1, mid + 1, r, al, ar);
	else return max(querymax(u << 1, l, mid, al, mid), querymax(u << 1 | 1, mid + 1, r, mid + 1, ar));
}

bool solve(int n)
{
	sort(node + 1, node + n + 1);

	//建立线段树
	build(1, 1, n);

	for (int i = 1; i <= n; ++i)
	{
		//二分查找与该lecture相交的lecture
		int pos = lower_bound(node + 1, node + n + 1, Node(node[i].ea, 1e9 + 5, 0, 0)) - (node + 1);

		//不相交就继续下一次循环
		if (!(i + 1 <= pos))
			continue;

		if (querymin(1, 1, n, i + 1, pos) < node[i].sb || querymax(1, 1, n, i + 1, pos) > node[i].eb)
			return false;
	}

	return true;
}

int main()
{
	//n个lecture
	int n;

	scanf("%d", &n);

	for (int i = 1; i <= n; ++i)
	{
		scanf("%d%d%d%d", &node[i].sa, &node[i].ea, &node[i].sb, &node[i].eb);
	}
	
	
	int flag = 1;
	flag = flag & solve(n);

	for (int i = 1; i <= n; ++i)
	{
		swap(node[i].sa, node[i].sb);
		swap(node[i].ea, node[i].eb);
	}

	flag = flag & solve(n);

	if (flag)
		puts("YES");
	else
		puts("NO");
	


	return 0;
}