【JZOJ5482】第三题

来源:互联网 发布:ppt批量导入照片mac 编辑:程序博客网 时间:2024/06/10 07:21

Description

sl想打dota2了,但他现在在机房,而且yz就在他旁边,他自然不能玩dota2这种这么容易被发现(鼠标、键盘发出啪啪啪的声音,眼睛中射出诡异的光)的游戏,所以他只好打开了一个小游戏。这个小游戏是这样的:在一个平面上有n个A类点(从1~n编号),m个B类点(从1~m编号)。A类点很团结,他们想用n-1条边将他们连成一棵树;B类点也想用m-1条边连成一棵树(边不可弯曲)。但这些边都不能在非AB类点出相交。如果成功连边则过关。
sl有个不好的习惯:如果没有过关,他就会气得砸键盘。如果在平时,没有什么关系(因为sl不喜欢运动,只喜欢打游戏,以他的力气砸不坏键盘);然而此时yz在他旁边,他一砸键盘,yz就知道他在打游戏了,一定会狠狠地D他一顿。凭sl那点可怜的智商,当然过不了关啦,所以为了拯救sl,你只好教他玩了。

Solution

题意即:在平面上给出两类点,没有三点共线,用两棵不相交的树分别将两类点连通,树边不能相交。

题解给出了一种十分巧妙的构造法:设g(x)表示x属于哪一类点,同时设一个函数f(x,y,z)表示以x,y,z三点围成的三角形以及内部所有点的一种构造,且满足g(x)=g(y)g(x)g(z)。于是对于每个f(x,y,z),我们要么能在三角形内部找到一个点p使得g(p)=g(z),然后以f(p,z,x)f(p,z,y)f(x,y,p)继续往下构造,要么内部所有点与x的类别相同。那么我们此时可以放射状连边构造。容易感受,这种构造方法是满足的。

接下来就是如何划分出一个个三角形。我们可以对原点集求一次凸包,此时有几种情况:

  1. 凸包上的某一类点不连续(沿一个时针方向),那么肯定无解。
  2. 凸包上全是一类点:那么肯定可以在内部找到一个另类点,与凸包上的点构成许多三角形,同时满足f的性质。
  3. 凸包上有两类点,每类点连续。那么我们也可以构成许多三角形,满足f的性质。

Code

#include<cstdio>#include<cstdlib>#include<cstring>#include<algorithm>#define fo(i,j,k) for(int i=j;i<=k;++i)#define fd(i,j,k) for(int i=j;i>=k;--i)#define N 3010#define ll long longusing namespace std;struct node{    int x,y,fr,wz;}a[N],an[2][N];int pos,su=0;int n,m;bool vis[N],in[N][N];int num[2],f[N];ll abss(ll x){    return x<0?-x:x;}ll cross(int i,int j,int o){    int X1=a[i].x-a[o].x,Y1=a[i].y-a[o].y,X2=a[j].x-a[o].x,Y2=a[j].y-a[o].y;    return (ll)X1*Y2-(ll)X2*Y1;}ll area(int i,int j,int o){    return abss(cross(i,j,o));}bool cmp(node x,node y){    int X1=x.x-a[1].x,Y1=x.y-a[1].y,X2=y.x-a[1].x,Y2=y.y-a[1].y;    return ((ll)X1*Y2-(ll)X2*Y1)>0;}int st[N],top=0;int d[N];void get(int u,int v,int w){    d[0]=0;    fo(i,1,n) if(!vis[i] && area(u,v,i)+area(u,w,i)+area(v,w,i)==area(u,v,w)) d[++d[0]]=i;}void put(int u,int v){    int co=a[u].fr;    if(in[u][v]) return;    su++;    in[u][v]=in[v][u]=1;    an[co][++num[co]].x=a[u].wz,an[co][num[co]].y=a[v].wz;}void work(int u,int v,int w){    get(u,v,w);    int p=0;    fo(i,1,d[0]) if(a[d[i]].fr==a[w].fr) {p=d[i];break;}    if(!p){        put(u,v);        fo(i,1,d[0]) if(a[d[i]].fr==a[u].fr) put(d[i],u);        return;    }    vis[p]=true;    work(u,v,p),work(p,w,u),work(p,w,v);}int main(){    scanf("%d %d",&n,&m);    fo(i,1,n) scanf("%d %d",&a[i].x,&a[i].y),a[i].wz=i;    fo(i,1,m) scanf("%d %d",&a[i+n].x,&a[i+n].y),a[i+n].fr=1,a[i+n].wz=i;    pos=1;    n+=m;    fo(i,2,n) if(a[i].y<a[pos].y) pos=i;    fo(i,1,n) f[i]=i;    swap(a[pos],a[1]);    sort(a+2,a+n+1,cmp);    st[++top]=1,st[++top]=2;    fo(i,3,n){        while(top>1 && cross(i,st[top],st[top-1])>0) top--;        st[++top]=i;    }    bool tp=0;    fo(i,1,top){        vis[st[i]]=1;        if(i>1 && a[st[i]].fr!=a[st[i-1]].fr) tp=1;    }    if(!tp){        int pt=0;        fo(i,1,n)        if(!vis[i] && a[i].fr!=a[st[1]].fr) {pt=i;break;}        vis[pt]=1;        fo(i,2,top) work(st[i],st[i-1],pt);        in[st[1]][st[top]]=1,work(st[1],st[top],pt);        fo(i,1,num[0]) printf("%d %d\n",an[0][i].x,an[0][i].y);        fo(i,1,num[1]) printf("%d %d\n",an[1][i].x,an[1][i].y);        return 0;    }    int col=a[st[1]].fr;    while(a[st[1]].fr==col) fo(i,2,top) swap(st[i],st[i-1]);    int np=1,nq;    fo(i,2,top) if(a[st[i]].fr!=a[st[1]].fr) break;    else np=i;    fo(i,np+2,top) if(a[st[i]].fr!=a[st[np+1]].fr){        printf("GG!");        return 0;    }    nq=top-np;    fo(i,2,np) work(st[i-1],st[i],st[np+1]);    fo(i,np+2,top) work(st[i-1],st[i],st[1]);    fo(i,1,num[0]) printf("%d %d\n",an[0][i].x,an[0][i].y);    fo(i,1,num[1]) printf("%d %d\n",an[1][i].x,an[1][i].y);    printf("%d\n",su);}