题目链接
https://arc083.contest.atcoder.jp/tasks/arc083_d
题意简述
的正方形上有个小球,第个在位置。有个A类机器人,第个在位置;有个B类机器人,第个在位置。依次启动机器人,A类机器人启动后将会向右走,将碰到的第个球收集起来,并不再启动;B类机器人启动后向上走,将碰到的第个求收集起来,并不再启动。求所有的方案中能收集所有小球的方案数模。
题解
假设有个点,如果有位置为的球,那么将第个点和第个点连接起来,代表第个B类机器人和第个A类机器人才能取走这个球。最终形成了个点条边的图,对于每个连通块分别考虑。
假设这个连通块有个点,如果边数,显然方案数为0;否则必定存在一种方案使得点和边一一对应(就是点对应的机器人取走了边对应的球)。由于点数边数,因此这个连通块必定是一颗基环树。对于不在环上的点,只能存在一种方式对应边;对于环上的点,有两种方式对应边,枚举即可。
现在得到了每个机器人对应的球,显然,对应方向上坐标比它小的球都要被先取走。换句话说,就是这个机器人比其他的某些机器人后启动。将这个机器人向其他必须比他先启动的机器人连边。显然,所有连的边都是图上有的边,并且环上两个端点编号之和最大的边显然不会加入,因此连的边构成了一棵森林。
对于每个森林分别填数,要求父亲的权值比儿子大,设表示的子树填数的方案数,显然
森林的方案数同理。
代码
#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;
}