【JZOJ5482】第三题

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的性质。

    • 凸包上有两类点,每类点连续。那么我们也可以构成许多三角形,满足 f <script type="math/tex" id="MathJax-Element-2785">f</script>的性质。
    • 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 long
      using 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);
      }
      
posted @ 2017-11-30 16:22  sadstone  阅读(25)  评论(0编辑  收藏  举报