IOI202021集训队作业DL205. Visual Python++
有一堆矩形,两两之间边没有公共点(即相离或包含),现在给出左下角和右上角,要求找到一种合法的对应关系。
\(n\le 10^5\)
假如存在合法关系,可以贪心构造:把所有点以\(x\)为第一关键字,\(y\)为第二关键字排序。维护个以\(y\)为关键字的集合\(S\)。
依次加入点。如果是左下角,则加入\(S\)中;如果是右上角,则在\(S\)中前驱,匹配之。
如果存在一种合法对应关系,可以证明这种方法一定可以把这种对应关系还原出来。(同时这也表明了合法的对应关系是唯一的)
问题是判断是否合法:做以上操作时,如果出现右上角没得匹配左下角则不合法;如果某个时刻集合中出现相等元素(即\(y\)相等)也不合法。
判断完这些之后再做一次扫描,如果遇到左下角,将其加入\(S\)中,并且判断是否和\(S\)中前驱后继对应的矩形相交;如果遇到右上角,删去其对应的左下角。讨论一下可以证明如果不和前驱后继对应矩形相交,那么不会和别的矩形相交。
用set维护,时间\(O(n\lg 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\n");
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\n",ans[i]);
}
return 0;
}