IOI202021集训队作业DL205. Visual Python++

有一堆矩形,两两之间边没有公共点(即相离或包含),现在给出左下角和右上角,要求找到一种合法的对应关系。

(nle 10^5)


假如存在合法关系,可以贪心构造:把所有点以(x)为第一关键字,(y)为第二关键字排序。维护个以(y)为关键字的集合(S)

依次加入点。如果是左下角,则加入(S)中;如果是右上角,则在(S)中前驱,匹配之。

如果存在一种合法对应关系,可以证明这种方法一定可以把这种对应关系还原出来。(同时这也表明了合法的对应关系是唯一的)

问题是判断是否合法:做以上操作时,如果出现右上角没得匹配左下角则不合法;如果某个时刻集合中出现相等元素(即(y)相等)也不合法。

判断完这些之后再做一次扫描,如果遇到左下角,将其加入(S)中,并且判断是否和(S)中前驱后继对应的矩形相交;如果遇到右上角,删去其对应的左下角。讨论一下可以证明如果不和前驱后继对应矩形相交,那么不会和别的矩形相交。

用set维护,时间(O(nlg n))


为什么C++17CE了但是C++11过了

using namespace std;
#include <bits/stdc++.h>
#define N 100005
int n;
struct DOT{int x,y,id;} a[N],b[N];
bool cmp(DOT p,DOT q){return p.x<q.x || p.x==q.x && p.y<q.y;}
struct cmpay{
	bool operator()(int p,int q){return a[p].y<a[q].y;}
};
set<int,cmpay> s;
int p[N],q[N];
bool find(){
	sort(a+1,a+n+1,cmp),sort(b+1,b+n+1,cmp);
	for (int i=1,j=1;i<=n;++i){
		for (;j<=n && !cmp(b[i],a[j])/*a[j]<=b[i]*/;++j){
			if (s.find(j)!=s.end())
				return 0;
			s.insert(j);
		}
		a[0]=b[i];
		auto t=s.upper_bound(0);
		if (t==s.begin())
			return 0;
		--t;
		p[*t]=i,q[i]=*t;
		s.erase(t);
	}
	return 1;
}
bool its(DOT i,DOT pi,DOT j,DOT pj){
	if (max(i.x,j.x)>min(pi.x,pj.x) || max(i.y,j.y)>min(pi.y,pj.y))
		return 0;
	if (i.x>j.x) swap(i,j),swap(pi,pj);
	return !(i.x<j.x && i.y<j.y && pj.x<pi.x && pj.y<pi.y);
}
bool check(){
	assert(s.empty());
	for (int i=1,j=1;i<=n;++i){
		for (;j<=n && !cmp(b[i],a[j]);++j){
			auto t=s.upper_bound(j);
			if (t!=s.end() && its(a[*t],b[p[*t]],a[j],b[p[j]]))
				return 0;
			if (t!=s.begin()){
				--t;
				if (its(a[*t],b[p[*t]],a[j],b[p[j]]))
					return 0;
			}
			s.insert(j);
		}
		s.erase(q[i]);
	}
	return 1;
}
int ans[N];
int main(){
//	freopen("in.txt","r",stdin);
	scanf("%d",&n);
	for (int i=1;i<=n;++i) scanf("%d%d",&a[i].x,&a[i].y),a[i].id=i;
	for (int i=1;i<=n;++i) scanf("%d%d",&b[i].x,&b[i].y),b[i].id=i;
	if (find()==0 || check()==0)
		printf("syntax error
");
	else{
		for (int i=1;i<=n;++i)
			ans[a[i].id]=b[p[i]].id;
		for (int i=1;i<=n;++i)
			printf("%d
",ans[i]);
	}
	return 0;
}