题目链接

https://arc083.contest.atcoder.jp/tasks/arc083_d

题意简述

n×nn\times n的正方形上有2n2n个小球,第ii个在(xi,yi)(x_i,y_i)位置。有nn个A类机器人,第ii个在(0,i)(0,i)位置;有nn个B类机器人,第ii个在(i,0)(i,0)位置。依次启动机器人,A类机器人启动后将会向右走,将碰到的第11个球收集起来,并不再启动;B类机器人启动后向上走,将碰到的第11个求收集起来,并不再启动。求所有的方案中能收集所有小球的方案数模109+710^9+7

题解

假设有2n2n个点,如果有位置为(x,y)(x,y)的球,那么将第xx个点和第y+ny+n个点连接起来,代表第xx个B类机器人和第yy个A类机器人才能取走这个球。最终形成了2n2n个点2n2n条边的图,对于每个连通块分别考虑。

假设这个连通块有xx个点,如果边数̸=x\not= x,显然方案数为0;否则必定存在一种方案使得点和边一一对应(就是点对应的机器人取走了边对应的球)。由于点数==边数,因此这个连通块必定是一颗基环树。对于不在环上的点,只能存在一种方式对应边;对于环上的点,有两种方式对应边,枚举即可。

现在得到了每个机器人对应的球,显然,对应方向上坐标比它小的球都要被先取走。换句话说,就是这个机器人比其他的某些机器人后启动。将这个机器人向其他必须比他先启动的机器人连边。显然,所有连的边都是图上有的边,并且环上两个端点编号之和最大的边显然不会加入,因此连的边构成了一棵森林。

对于每个森林分别填数,要求父亲的权值比儿子大,设f[i]f[i]表示ii的子树填数的方案数,显然
f[i]=(size[i]1)!sson[i]size[s]!sson[i]f[s] f[i]=\frac{(size[i]-1)!}{\prod_{s\in son[i]} size[s]!}\prod _{s\in son[i]} f[s]
森林的方案数同理。

代码

#include <queue>
#include <cstdio>
#include <algorithm>

int read()
{
  int x=0,f=1;
  char ch=getchar();
  while((ch<'0')||(ch>'9'))
    {
      if(ch=='-')
        {
          f=-f;
        }
      ch=getchar();
    }
  while((ch>='0')&&(ch<='9'))
    {
      x=x*10+ch-'0';
      ch=getchar();
    }
  return x*f;
}

const int maxn=100000;
const int maxv=maxn*2;
const int maxe=maxv*2;
const int mod=1000000007;

std::queue<int> q;
std::vector<int> cur;
int n,pre[maxe+10],now[maxv+10],son[maxe+10],tot,_pre[maxe+10],_now[maxv+10],_son[maxe+10],_tot,vis[maxv+10],ecnt,cir[maxv+10],deg[maxv+10],choose[maxv+10],fac[maxv+10],ifac[maxv+10],f[maxv+10],size[maxv+10];

int C(int a,int b)
{
  return 1ll*fac[a]*ifac[b]%mod*ifac[a-b]%mod;
}

int _ins(int a,int b)
{
  _pre[++_tot]=_now[a];
  _now[a]=_tot;
  _son[_tot]=b;
  return 0;
}

int calc()
{
  for(auto i:cur)
    {
      deg[i]=_now[i]=0;
    }
  for(auto i:cur)
    {
      for(int j=now[i]; j; j=pre[j])
        {
          int v=son[j];
          if(v<choose[i])
            {
              _ins(v,i);
              ++deg[i];
            }
        }
    }
  for(auto i:cur)
    {
      if(!deg[i])
        {
          q.push(i);
        }
      f[i]=1;
      size[i]=1;
    }
  int sz=0,ans=1;
  while(!q.empty())
    {
      int u=q.front(),flag=0;
      q.pop();
      for(int i=_now[u]; i; i=_pre[i])
        {
          flag=1;
          int v=_son[i];
          --deg[v];
          size[v]+=size[u];
          f[v]=1ll*f[v]*f[u]%mod*C(size[v]-1,size[u])%mod;
          if(!deg[v])
            {
              q.push(v);
            }
        }
      if(!flag)
        {
          sz+=size[u];
          ans=1ll*ans*f[u]%mod*C(sz,size[u])%mod;
        }
    }
  return ans;
}

int ins(int a,int b)
{
  pre[++tot]=now[a];
  now[a]=tot;
  son[tot]=b;
  return 0;
}

int dfs(int u)
{
  vis[u]=1;
  cur.push_back(u);
  for(int i=now[u]; i; i=pre[i])
    {
      ++ecnt;
      int v=son[i];
      if(!vis[v])
        {
          dfs(v);
        }
    }
  return 0;
}

int search(int u)
{
  cir[++cir[0]]=u;
  vis[u]=1;
  for(int i=now[u]; i; i=pre[i])
    {
      int v=son[i];
      if(!vis[v])
        {
          search(v);
        }
    }
  return 0;
}

int solve()
{
  if((int)cur.size()!=ecnt)
    {
      return 0;
    }
  for(auto i:cur)
    {
      if(deg[i]==1)
        {
          q.push(i);
        }
    }
  while(!q.empty())
    {
      int u=q.front();
      q.pop();
      for(int i=now[u]; i; i=pre[i])
        {
          int v=son[i];
          if(deg[v]!=1)
            {
              --deg[v];
              choose[u]=v;
              if(deg[v]==1)
                {
                  q.push(v);
                }
            }
        }
    }
  int st=0;
  for(auto i:cur)
    {
      if(deg[i]>1)
        {
          st=i;
          vis[i]=0;
        }
    }
  cir[0]=0;
  search(st);
  cir[cir[0]+1]=cir[1];
  for(int i=1; i<=cir[0]; ++i)
    {
      choose[cir[i]]=cir[i+1];
    }
  int res=calc();
  for(int i=2; i<=cir[0]+1; ++i)
    {
      choose[cir[i]]=cir[i-1];
    }
  res+=calc();
  if(res>=mod)
    {
      res-=mod;
    }
  for(auto i:cur)
    {
      vis[i]=1;
    }
  return res;
}

int main()
{
  n=read();
  fac[0]=1;
  for(int i=1; i<=n<<1; ++i)
    {
      fac[i]=1ll*fac[i-1]*i%mod;
    }
  ifac[0]=ifac[1]=1;
  for(int i=2; i<=n<<1; ++i)
    {
      ifac[i]=1ll*(mod-mod/i)*ifac[mod%i]%mod;
    }
  for(int i=2; i<=n<<1; ++i)
    {
      ifac[i]=1ll*ifac[i-1]*ifac[i]%mod;
    }
  for(int i=1; i<=n<<1; ++i)
    {
      int x=read(),y=read();
      ins(x,y+n);
      ins(y+n,x);
      ++deg[x];
      ++deg[y+n];
    }
  int tsize=0,ans=1;
  for(int i=1; i<=n<<1; ++i)
    {
      if(!vis[i])
        {
          cur.clear();
          ecnt=0;
          dfs(i);
          ecnt/=2;
          int res=solve();
          tsize+=ecnt;
          ans=1ll*ans*res%mod*C(tsize,ecnt)%mod;
        }
    }
  printf("%d\n",ans);
  return 0;
}