ARC083Fgit
題意概要:給定 \(2n\) 個二維平面上的球,座標分別爲 \((x_i,y_i)\),並給出 \(n\) 個 \(A\)類 機器人 和 \(n\) 個 \(B\)類 機器人,其中:學習
如今須要依次觸發這 \(2n\) 個機器人,(每一個機器人只能被觸發一次),問有多少種觸發機器人的方式能將全部球拿完(共 \((2n)!\) 種可能狀況)spa
\(2\le n\le 10^5,\ 1\le x_i,y_i\le n\)code
作完這道毒瘤題,17年的做業終於完成了! (。・∀・)ノ゙get
往上一翻發現本身是從一半開始作的 இ皿இit
乍一看沒有啥子想法io
考慮到一個球 \((x,y)\),它有兩種被清除的方式:被第 \(x\) 個 \(A\) 給除掉;被第 \(y\) 個 \(B\) 給除掉。而同時,因爲球的數量和機器人的數量相同,因此每一個機器人必須認領一個球。class
這乍一看沒啥子用,但放在圖上就是個基環樹森林:test
原題轉化爲:對於每種給森林定向的方案,求出其知足上述拓撲關係的排列個數。di
先考慮對於某種定向方案,求出合法排列方案。
若是對於這種拓撲關係連邊,最終這些拓撲序關係的邊將組成一個內向樹森林:
對於內向樹森林求拓撲序的部分,應該都很熟悉了:設森林點集爲 \(S\),節點 \(i\) 的子樹大小爲 \(sz_i\),則這個內向樹森林的拓撲序個數爲
\[\frac {|S|!}{\prod_{x\in S}sz_x}\]
簡要說一下證實:能夠將Dp式列出來,將每一個節點 \(x\) 在自身計算的係數 \((sz_x-1)!\) 與在其父親 \(f\) 處計算的 \(\frac 1{sz_x!}\) 抵消,可得 \(\frac 1{sz_x}\),綜合可得上述公式。森林的話,考慮拿一個虛根將全部樹串起來便可。
解決完了求方案數的任務,再考慮枚舉定向方案。
枚舉全部定向方案確定是不可取的,但幸運的是,這是一個基環樹森林。
對於一棵基環樹而言,其非環邊只能由遠離環的點認領,而環則有順逆兩種方式認領,能夠暴力枚舉。
由幼兒園學習的乘法分配率可知,不一樣的樹之間不必一塊兒枚舉,因此對於每棵樹算出兩種定向方式的答案和,再將全部樹的答案乘起來便可。
時間複雜度明顯是線性,頂可能是枚舉每棵樹的兩種定向方式時有個 \(2\) 的常數
#include <cstdio> #include <cctype> typedef long long ll; template <typename _tp> inline void read(_tp&x){ char ch=getchar(),ob=0;x=0; while(ch!='-'&&!isdigit(ch))ch=getchar();if(ch=='-')ob=1,ch=getchar(); while(isdigit(ch))x=x*10+ch-'0',ch=getchar();if(ob)x=-x; } const int N = 201000; struct Edge {int v, nxt;} a[N*4]; int head[N], Head[N], indeg[N]; int sz[N], pr[N]; bool vis[N]; int n, _; inline void add(int x, int y, int*arr) { a[++_].v = y, a[_].nxt = arr[x], arr[x] = _; } const int p = 1e9+7; int fac[N], ifac[N], inv[N]; int st[N], tp; #define FID(i) for(int id=1,i;i=st[id],id<=tp;++id) int X, Y; int et, pt; void bfs(int x, int las) { vis[st[++tp] = x] = true, ++pt; for(int i=head[x];i;++et,i=a[i].nxt) if(!vis[a[i].v]) bfs(a[i].v, x); else if(a[i].v != las) X = x, Y = a[i].v; } void tfs(int x, int las) { for(int i=head[x];i;i=a[i].nxt) if(a[i].v != las and a[i].v != X) pr[a[i].v] = x, tfs(a[i].v, x); } void dfs(int x, int las) { sz[x] = 1; for(int i=Head[x];i;i=a[i].nxt) if(a[i].v != las) dfs(a[i].v, x), sz[x] += sz[a[i].v]; } int solve() { tfs(X, Y); pr[X] = Y; FID(i) Head[i] = indeg[i] = 0; FID(x) for(int i=head[x];i;i=a[i].nxt) if(a[i].v < pr[x]) add(x, a[i].v, Head), ++indeg[a[i].v]; FID(i) if(!indeg[i]) dfs(i, i); int Ans = fac[pt]; FID(i) Ans = (ll)Ans * inv[sz[i]]%p; return Ans; } int main() { read(n); for(int i=1,x,y;i<=(n<<1);++i) { read(x), read(y), y += n; add(x, y, head), add(y, x, head); } n <<= 1; fac[0] = fac[1] = inv[0] = inv[1] = ifac[0] = ifac[1] = 1; for(int i=2;i<=n;++i) { fac[i] = (ll)fac[i-1] * i%p; inv[i] = (ll)(p-p/i) * inv[p%i]%p; ifac[i] = (ll)ifac[i-1] * inv[i]%p; } int Ans = fac[n]; for(int i=1;i<=n;++i) if(!vis[i]) { tp = pt = et = 0; bfs(i, i); Ans = (ll)Ans * ifac[pt]%p; if((pt << 1) != et) return puts("0"), 0; int res = solve(); X ^= Y, Y ^= X, X ^= Y; res += solve(); Ans = (ll)Ans * res%p; } printf("%d\n", Ans); return 0; }