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;
}