set 扫描线维护平面线段位置关系 & [WC2012]记忆中的水杉树

欺骗 STL。
做完写。


为什么会 WA 95pts,难以理解。没时间调了,硬着头皮写吧。


题目的第二问要求我们构造一种合法方案。首先发现一个性质,就是完全可以让 q 全部都是同一方向(不妨令朝上(1)),必然存在一条自由的线段可以率先移除,然后就一点一点移走了。这其中有一种拓扑型的关系,不难发现。由于题目所保证的线段之间的位置关系的性质,判断的依据就是简单地看任意横坐标下先移除的线段的纵坐标大于后移除的线段的纵坐标。做过一些平面线段位置关系的维护的题目不难想到扫描线,并且使用 set 维护这种动态稳定的关系。所谓动态稳定是说扫描线从左往右扫,线段之间的相对关系是不会变化的。利用这一性质可以使用一个 setS 维护之,而 Segment 的小于号则定义为 x=now 时的各线段对应一次函数的函数值的从小到大关系,now 为当前的扫描线值,另外 now 显然只需要取 2n 个端点横坐标。这里可能会有疑问,一个小于号的比较依据还是变化的,这不是有悖常理吗?其实不然,因为如果两个线段在一个时刻满足大小关系,在另一时刻必然满足同样大小关系,我们欺骗 STL 没有任何的成本。那么接着刚才说,我们每扫到一条线段的左端点就把它插入 set,右端点时 erase,插入时只需和前驱、后继之间连相应方向的有向边,这样即可建出一个边数为 O(n) 的有向图,这个有向图表征了纵向拓扑关系,据此便可按照拓扑序从小到大的顺序构造答案了。
题目的第一问要求我们判断一个方案是否合法。一个方案合法当且仅当 q=0/2 的方案满足横向拓扑关系、q=1/3 的方案满足纵向拓扑关系。我的做法是求出两张 DAG,对于每一个输入的操作,判断其是否满足对应方向上的拓扑关系,若满足则继续进行下去,并从两张图中分别删除这个点,同时相应地每个点的出入度被维护。

至于为什么 95,还望高人给小数据 hack。

#include <bits/stdc++.h>
using namespace std;
inline int read(){
	register char ch=getchar();register int x=0,f=1;
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9')x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
	return x*f;
}
const int N=2e5+5;
int n,now,U,ours,inx[N],outx[N],iny[N],outy[N],ans2[N],In[N];
struct Seg {
	int a,b,c,d,id;
}seg[N];
struct oper {
	int x,id,type;
}op[N];
double whenx(int x,int x_1,int y_1,int x_2,int y_2){
	return 1.0*(y_1-y_2)/(x_1-x_2)*(x-x_2)+y_2;
}
bool operator<(Seg a,Seg b){
	return whenx(now,a.a,a.b,a.c,a.d)<whenx(now,b.a,b.b,b.c,b.d);
}
bool operator==(Seg a,Seg b){
	return a.id==b.id;
}
set<Seg>S;
vector<int>G1[N],g1[N],G2[N],g2[N];
queue<int>Q;
inline void adde1(int u,int v){//cerr<<u<<'-'<<v<<',';
	G1[u].emplace_back(v);
	g1[v].emplace_back(u);
	inx[v]++,outx[u]++;
}
inline void adde2(int u,int v){
	G2[u].emplace_back(v);
	g2[v].emplace_back(u);
	iny[v]++,outy[u]++;
}
void Over(int id){
	printf("%d\n",id);
	for(int i=1;i<=n;i++)printf("%d 1\n",ans2[i]);
	exit(0); 
}
signed main(){
	//freopen("10.in","r",stdin);freopen("move.out","w",stdout);
	n=read();
	for(int i=1;i<=n;i++)seg[i].a=read(),seg[i].b=read(),seg[i].c=read(),seg[i].d=read(),seg[i].id=i;
	for(int i=1;i<=n;i++)op[++U]=oper{min(seg[i].a,seg[i].c),i,1},op[++U]=oper{max(seg[i].a,seg[i].c),i,-1};
	sort(op+1,op+U+1,[](oper a,oper b){return a.x==b.x?a.type<b.type:a.x<b.x;});
	for(int i=1;i<=n;i++)G1[i].clear(),g1[i].clear();
	now=op[1].x;
	for(int o=1;o<=U;o++,now=op[o].x){
		if(op[o].type==-1){
			S.erase(S.lower_bound(seg[op[o].id]));
		}
		else {
			set<Seg>::iterator it=S.lower_bound(seg[op[o].id]);
			if(it!=S.end())adde1(it->id,op[o].id);
			if(it!=S.begin())adde1(op[o].id,prev(it)->id);
			S.insert(seg[op[o].id]);
		}
	}
	for(int i=1;i<=n;i++)In[i]=inx[i];
	for(int i=1;i<=n;i++)if(!In[i])Q.push(i);
	while(!Q.empty()){
		int x=Q.front();Q.pop();
		ans2[++ours]=x;
		for(int y:G1[x])if(!--In[y])Q.push(y);
	}
	for(int i=1;i<=n;i++)swap(seg[i].a,seg[i].b),swap(seg[i].c,seg[i].d);
	U=0;for(int i=1;i<=n;i++)op[++U]=oper{min(seg[i].a,seg[i].c),i,1},op[++U]=oper{max(seg[i].a,seg[i].c),i,-1};
	sort(op+1,op+U+1,[](oper a,oper b){return a.x==b.x?a.type<b.type:a.x<b.x;});
	for(int i=1;i<=n;i++)G2[i].clear(),g2[i].clear();
	now=op[1].x;
	for(int o=1;o<=U;o++,now=op[o].x){
		if(op[o].type==-1){
			S.erase(S.lower_bound(seg[op[o].id]));
		}
		else {
			set<Seg>::iterator it=S.lower_bound(seg[op[o].id]);
			if(it!=S.end())adde2(it->id,op[o].id);
			if(it!=S.begin())adde2(op[o].id,prev(it)->id);
			S.insert(seg[op[o].id]);
		}
	}
	for(int i=1,x,p;i<=n;i++){
		x=read(),p=read();
		if(p==0){
			if(outy[x])Over(i);
			for(int y:g2[x])outy[y]--;
			for(int y:G1[x])inx[y]--;
			for(int y:g1[x])outx[y]--;
		}if(p==1){
			if(inx[x])Over(i);
			for(int y:G1[x])inx[y]--;
			for(int y:G2[x])iny[y]--;
			for(int y:g2[x])outy[y]--;
		}if(p==2){
			if(iny[x])Over(i);
			for(int y:G2[x])iny[y]--;
			for(int y:G1[x])inx[y]--;
			for(int y:g1[x])outx[y]--;
		}if(p==3){
			if(outx[x])Over(i);
			for(int y:g1[x])outx[y]--;
			for(int y:G2[x])iny[y]--;
			for(int y:g2[x])outy[y]--;
		}
	}//Over(4);
}
posted @ 2022-09-26 14:10  pengyule  阅读(38)  评论(0编辑  收藏  举报