1. 程式人生 > 其它 >【洛谷6940】[ICPC2017 WF] Visual Python++(掃描線)

【洛谷6940】[ICPC2017 WF] Visual Python++(掃描線)

點此看題面

  • 給定\(n\)個左上角和\(n\)個右下角,要求將它們匹配,使得\(n\)個矩形的邊界無交,或判斷無解。
  • \(n\le10^5\)

構造方案

對於當前右下角\((x,y)\),容易想到去找到滿足\(a\le x,b\le y\)的所有尚未匹配的左上角\((a,b)\)\(b\)最大的那個,讓\((a,b)\)\((x,y)\)匹配。

然後發現其實這是唯一構造方案,因為假設選擇了另一個滿足\(c\le x,d\le y\)的尚未匹配的左上角\((c,d)\),則\((a,b)\)要麼被困在了矩形內(\(a\ge c\)),要麼就因為這個矩形的阻隔再也不可能匹配了(\(a<c\)

)。

既然如此,只需判斷這個方案是否合法即可。

檢驗合法

先考慮按照橫座標順序列舉,判斷縱方向上是否存在相交區間。(之後還要把橫縱座標交換再檢驗一遍)

這裡要先提一個細節,就是橫座標相同時同類型區間(型別指加入/刪除)不能存在包含關係,要特殊判斷。(當然更不能相交,但相交在之後會一起判掉)

而要判是否存在區間相交,考慮我們把記下所有端點,則當前區間\([l,r]\)中不能包含任何一個已有的端點。

只需找到大於等於\(l\)的第一個點判斷是否在\([l,r]\)中即可,可以直接用一個\(set\)維護下所有端點。

程式碼:\(O(nlogn)\)

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 100000
#define NA() (puts("syntax error"),exit(0))//無解
using namespace std;
int n,ans[N+5];
struct P {int p,x,y;I bool operator < (Con P& o) Con {return x^o.x?x<o.x:y<o.y;}}p[2*N+5];
struct OP {int x,l,r,op;I bool operator < (Con OP& o) Con {return x^o.x?x<o.x:(op^o.op?op<o.op:l<o.l);}}w[2*N+5];
set<pair<int,int> > S;I void Get()//構造方案
{
	set<pair<int,int> >::iterator it;sort(p+1,p+2*n+1);for(RI i=1;i<=2*n;++i)
	{
		if(p[i].p<=n) {S.insert(make_pair(p[i].y,p[i].p));continue;}//左上角
		((it=S.lower_bound(make_pair(p[i].y+1,0)))==S.begin())&&(NA(),0),ans[(--it)->second]=p[i].p,S.erase(it);//右下角,找到縱座標儘可能大的,找不到就無解
	}
}
set<int> V;I void Check()//檢驗合法
{
	RI i;for(i=1;i<=n;++i) w[i]=(OP){p[i].x,p[i].y,p[ans[i]].y,1},w[i+n]=(OP){p[ans[i]].x+1,p[i].y,p[ans[i]].y,0};
	RI o,lst[2]={0,0};for(sort(w+1,w+2*n+1),i=1;i<=2*n;++i)//特殊處理橫座標相同的區間
		o=lst[w[i].op],lst[w[i].op]=i,w[o].x==w[i].x&&(w[o].l==w[i].l||w[o].r>=w[i].r)&&(NA(),0);//不能存在包含關係
	set<int>::iterator it,jt;for(V.insert(0),V.insert(2e9),i=1;i<=2*n;++i)//掃描線
	{
		if(!w[i].op) {it=V.find(w[i].l),w[i].l^w[i].r&&(V.erase(++(jt=it)),0),V.erase(it);continue;}//刪除
		*V.lower_bound(w[i].l)<=w[i].r&&(NA(),0),V.insert(w[i].l),V.insert(w[i].r);//加入,不能包含任何端點
	}
}
I bool cmp(Con P& x,Con P& y) {return x.p<y.p;}
int main()
{
	RI i;for(scanf("%d",&n),i=1;i<=2*n;++i) scanf("%d%d",&p[i].x,&p[i].y),p[i].p=i;
	for(Get(),sort(p+1,p+2*n+1,cmp),Check(),i=1;i<=2*n;++i) swap(p[i].x,p[i].y);Check();//先構造方案,然後交換橫縱座標檢驗兩次
	for(i=1;i<=n;++i) printf("%d\n",ans[i]-n);return 0;
}