1. 程式人生 > >ARC 083 F Bichrome Tree - 組合數學 - 基環樹

ARC 083 F Bichrome Tree - 組合數學 - 基環樹

題目大意:平面直角座標系上有2n個點,x軸上(1~n,0)有n個bot,y軸同理。啟動x軸上的bot會向上走,y軸上的會向右走,走到第一個小球並和小球發生湮滅。問(2n)!種啟動順序中有多少方案能使小球全部湮滅。
題解:
考慮二分圖,每個小球對應X連Y的一條邊。
那麼如果先不考慮順序,考慮分配方案,相當於是每條邊選擇一個端點。
例如x-y這條邊選擇x作為端點,並且x是X中的點,那麼意思是我選擇用(x,0)的bot湮滅(x,y)的小球。
顯然這樣每個連通塊必須是基環樹,不同連通塊基本無關。
然後樹上的邊選的端點是確定的:遠離環的那個。
環上的只有兩種情況,列舉一下即可。
然後考慮順序問題,顯然對於邊x->y->z,如果x<z,那麼x->y就要早於y->z執行。用邊的起點表示邊,就是x要早於y,或者說y晚於x。後者形成一個外向的森林,方案數是cnt!/(prod sz)。
最後用組合數插起來即可。

 
#include<bits/stdc++.h>
#define gc getchar()
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define Rep(i,v) rep(i,0,(int)v.size()-1)
#define lint long long
#define mod 1000000007
#define N 200010
#define db double
#define pb push_back
#define mp make_pair
#define fir first
#define sec second
#define debug(x) cerr<<#x<<"="<<x
#define sp <<" " #define ln <<endl using namespace std; typedef pair<int,int> pii; typedef set<int>::iterator sit; inline int inn() { int x,ch;while((ch=gc)<'0'||ch>'9'); x=ch^'0';while((ch=gc)>='0'&&ch<='9') x=(x<<1)+(x<<3)+(ch^'0'
);return x; } struct edges{ int to,pre; }e[N<<1];int h[N],etop,onc[N],fa[N],vis[N],sz[N],d[N]; int fac[N],facinv[N],lst[N],ecnt,cnt,p[N],in[N];vector<int> g[N]; inline int fast_pow(int x,int k,int ans=1) { for(;k;k>>=1,x=(lint)x*x%mod) (k&1)?ans=(lint)ans*x%mod:0;return ans; } inline int prelude(int n) { rep(i,fac[0]=1,n) fac[i]=(lint)fac[i-1]*i%mod; facinv[n]=fast_pow(fac[n],mod-2); for(int i=n-1;i>=0;i--) facinv[i]=facinv[i+1]*(i+1ll)%mod; return 0; } inline int add_edge(int u,int v) { return e[++etop].to=v,e[etop].pre=h[u],h[u]=etop; } int has_cyc=0; int dfs(int x) { d[x]=d[fa[x]]+1,vis[x]=1,lst[++cnt]=x;int t=0; for(int i=h[x],y;i;i=e[i].pre) if((y=e[i].to)^fa[x]) { if(!vis[y]) fa[y]=x,dfs(y),ecnt++;else if(vis[y]&&d[y]<d[x]) t=y,ecnt++; } if(t&&has_cyc) printf("0\n"),exit(0); if(t) { while(x^t) onc[x]=1,x=fa[x];onc[t]=1,has_cyc=1; } return 0; } int getclc(int x,int y,int z) { p[x]=y;if(y==z) return 0; for(int i=h[y],w;i;i=e[i].pre) if(onc[w=e[i].to]&&e[i].to!=x) getclc(y,w,z); return 0; } int getsz(int x,int &ans) { sz[x]=1;Rep(i,g[x]) sz[x]+=getsz(g[x][i],ans); return ans=(lint)ans*sz[x]%mod,sz[x]; } inline int calc() { rep(t,1,cnt) g[lst[t]].clear(),in[lst[t]]=0; rep(t,1,cnt) { int x=lst[t],y=p[x],z=p[y]; if(x<z) g[y].pb(x),in[x]++; } int ans=1,x;rep(t,1,cnt) if(!in[x=lst[t]]) getsz(x,ans); return ans=(lint)fast_pow(ans,mod-2)*fac[cnt]%mod; } int getp(int x,int f=0) { for(int i=h[x],y;i;i=e[i].pre) if((y=e[i].to)!=f&&!onc[e[i].to]) p[y]=x,getp(y,x); return 0; } inline int solve(int x) { has_cyc=0,ecnt=0,cnt=0,fa[x]=0,dfs(x); if(cnt^ecnt) printf("0\n"),exit(0);int ans=0; rep(i,1,cnt) if(onc[x=lst[i]]) getp(x); rep(t,1,cnt) if(onc[x=lst[t]]) { for(int i=h[x],y;i;i=e[i].pre) if(onc[y=e[i].to]) getclc(x,y,x),ans+=calc(),(ans>=mod?ans-=mod:0); break; } return ans; } int main() { int n=inn(),ans=1,x,y;prelude(n*2); rep(i,1,2*n) x=inn(),y=inn()+n,add_edge(x,y),add_edge(y,x); rep(i,1,2*n) if(!vis[i]) ans=(lint)ans*solve(i)%mod,ans=(lint)ans*facinv[cnt]%mod; ans=(lint)ans*fac[2*n]%mod;return !printf("%d\n",ans); }