CF1045E Ancient civilizations

XV.CF1045E Ancient civilizations

神题。

我们先考虑如果凸包上只有三个点时的情形。

假如该三个点是同色的,我们考虑能否在该三角形内部找到一个异色点。假如能找到,我们便可以将这个大三角形拆分成三个小三角形,每个小三角形以该异色节点和凸包上两个点为顶点,这就使得小三角形的三个顶点并不同色。这样我们就可以递归到下一种情形了。假如找不到,则明显该三角形内部所有点全部同色,直接从一个点出发连接所有点即可。

下面我们考虑三个点不全同色的情形。则必定有一个少数色。我们便在三角形内部寻找有无少数色的节点。假如有的话,明显我们可以连接该少数色节点和少数色的顶点,然后继续拆分成三个小三角形继续递归处理。假如没有,就直接暴力连即可。

下面我们考虑一般情况,即凸包上不只有三个点的情形。我们考虑将该凸包拆成一段黑一段白这样交替出现的情形。假如有两段颜色相同的凸包上节点并不相邻,明显无论怎么连都会出现交叉,直接跳无解。否则:

假如凸包上只有一种颜色的点,明显可以如三角形三点同色一样,剖分成数个小三角形进行处理。否则,即凸包上不止有一种颜色的点,那么我们找到凸包上白点中最左的那一个(指按照顺/逆时针排序后白点段中最靠左的那个),连接其和所有黑点构成三角形(并非真的相连,只是指构成三角形罢了);同理,找到黑点中最靠左的那个,并连接其和所有白点构成三角形。明显此时我们便将整个大凸包拆成了无数的异色三角形,可以递归处理了。

明显在每次递归处理中,我们都要一遍扫过所有节点判断它是否在三角形内部,故总复杂度\(O(n^2)\)

代码:

#include<bits/stdc++.h>
using namespace std;
int n,stk[1010],tp,ord[1010];
struct Vector{
	int x,y;
	Vector(int X=0,int Y=0){x=X,y=Y;}
	friend Vector operator +(const Vector &u,const Vector &v){return Vector(u.x+v.x,u.y+v.y);}
	friend Vector operator -(const Vector &u,const Vector &v){return Vector(u.x-v.x,u.y-v.y);}
	friend int operator &(const Vector &u,const Vector &v){return u.x*v.y-u.y*v.x;}//cross times
	friend int operator |(const Vector &u,const Vector &v){return u.x*v.x+u.y*v.y;}//point times
	double operator ~()const{return atan2(y,x);}
	void read(){scanf("%d%d",&x,&y);}
	void print(){printf("(%d,%d)",x,y);}
}p[1010];
bool col[1010];
vector<pair<int,int> >edges;
void solve(int u,int v,int w){
	bool minor=col[u]^col[v]^col[w];
	int MINOR;
	if(col[u]==minor)MINOR=u;
	if(col[v]==minor)MINOR=v;
	if(col[w]==minor)MINOR=w;
	for(int i=0;i<n;i++){
		if(i==u||i==v||i==w)continue;
		if(col[i]!=minor)continue;
		bool U=((p[u]-p[i])&(p[v]-p[i]))>0;
		bool V=((p[v]-p[i])&(p[w]-p[i]))>0;
		bool W=((p[w]-p[i])&(p[u]-p[i]))>0;
		if(U!=V||V!=W||W!=U)continue;
		edges.push_back(make_pair(MINOR,i));
		solve(u,v,i),solve(v,w,i),solve(w,u,i);
		return;
	}
	for(int i=0;i<n;i++){
		if(i==u||i==v||i==w)continue;
		bool U=((p[u]-p[i])&(p[v]-p[i]))>0;
		bool V=((p[v]-p[i])&(p[w]-p[i]))>0;
		bool W=((p[w]-p[i])&(p[u]-p[i]))>0;
		if(U!=V||V!=W||W!=U)continue;
		if(col[u]==col[i]){edges.push_back(make_pair(u,i));continue;}
		if(col[v]==col[i]){edges.push_back(make_pair(v,i));continue;}
		if(col[w]==col[i]){edges.push_back(make_pair(w,i));continue;}
	}
}
bool onhull[1010];
void print(){
	printf("%d\n",edges.size());
	for(auto i:edges)printf("%d %d\n",i.first,i.second);
	exit(0);
}
int main(){
	scanf("%d",&n);
	for(int i=0;i<n;i++)p[i].read(),scanf("%d",&col[i]),ord[i]=i;
	sort(ord,ord+n,[](int u,int v){return p[u].x==p[v].x?p[u].y<p[v].y:p[u].x<p[v].x;});
	for(int i=1;i<n;i++)p[ord[i]]=p[ord[i]]-p[ord[0]];
	p[ord[0]]=Vector(0,0);
	sort(ord+1,ord+n,[](int u,int v){return ~p[u]<~p[v];});
	stk[tp++]=ord[0];
	for(int i=1;i<n;i++){
		while(tp>=2&&((p[ord[i]]-p[stk[tp-1]])&(p[stk[tp-1]]-p[stk[tp-2]]))>=0)tp--;
		stk[tp++]=ord[i];
	}
	for(int i=0;i<tp;i++)onhull[stk[i]]=true;
	int l=tp,r=1;
	while(r<l&&col[stk[r]]==col[stk[0]])r++;
	while(l>r&&col[stk[l-1]]==col[stk[0]])l--;
	if(r==l){
		for(int j=1;j<tp;j++)edges.push_back(make_pair(stk[j-1],stk[j]));
		for(int i=0;i<n;i++){
			if(onhull[i]||col[i]==col[stk[0]])continue;
			for(int j=0;j<tp;j++)solve(stk[j],stk[(j+1)%tp],i);
			print();
		}
		for(int i=0;i<n;i++){
			if(onhull[i])continue;
			for(int j=i+1;j<n;j++)if(!onhull[j])edges.push_back(make_pair(i,j));
			if(col[i]==col[stk[0]])edges.push_back(make_pair(i,stk[0]));
			print();
		}
	}else{
		for(int i=r;i<l;i++)if(col[stk[i]]==col[stk[0]]){puts("Impossible");return 0;}
		for(int i=(l+1)%tp;i!=r;i=(i+1)%tp)edges.push_back(make_pair(stk[(i+tp-1)%tp],stk[i]));
		for(int i=r+1;i<l;i++)edges.push_back(make_pair(stk[i-1],stk[i]));
		for(int i=(l+1)%tp;i!=r;i=(i+1)%tp)solve(stk[(i+tp-1)%tp],stk[i],stk[r]);
		for(int i=r+1;i<l;i++)solve(stk[i-1],stk[i],stk[l%tp]);
		print();
	}
	print();
	return 0;
}

posted @ 2021-04-05 20:26  Troverld  阅读(66)  评论(0编辑  收藏  举报