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; }