题目大意:有n我的构成森林关系。如今要把他们排成一列,使儿子不在父亲前面,求方案数。n<=40000。ios
惊叹数学之妙。ide
首先看见森林,不如来一个超级点作根。spa
设f[i]表示处理完i与i的子树的方案数,size[i]表示子树大小,j是i的儿子。3d
首先对于两棵子树,它们之间是互不影响的,因此方案数应该相乘,这部分表示出来就是:code
而后思考对于一种对于每一个j的内部顺序已经肯定,求i有多少种放法?这就至关于一个可重集合的模型(在j内的至关于一个重复元素):blog
而后两部分用乘法原理乘起来,就是f[i]:get
这个就能够写这道题了。数学
但实际上能够进一步化简?咱们来考虑把一个f[j]拆开。设j的儿子是x:string
而后f[i]的式子里的size[j]!能够被(size[j]-1)!消成size[j]。it
思惟发散一下,f[i]内的size!和(size-1)!都会被消成1/size。
最后的答案是f[0]:
由于(size[0]-1)!=n!,最后答案就是:
#include <iostream> #include <cstdio> #include <cstdlib> #include <algorithm> #include <vector> #include <cstring> #include <queue> #include <complex> #include <stack> #define LL long long int #define dob double #define FILE "11174" using namespace std; const int N = 40010; const int Mod = 1000000007; struct Node{int to,next;}E[N]; int n,m,head[N],tot; int J[N],Ny[N],size[N],fa[N]; inline int gi(){ int x=0,res=1;char ch=getchar(); while(ch>'9'||ch<'0'){if(ch=='-')res*=-1;ch=getchar();} while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar(); return x*res; } inline void link(int u,int v){ E[++tot]=(Node){v,head[u]}; head[u]=tot; } inline void pre(){ Ny[1]=J[1]=1; for(int i=2;i<N;++i){ J[i]=1ll*J[i-1]*i%Mod; Ny[i]=1ll*(Mod-Mod/i)*Ny[Mod%i]%Mod; } } inline void dfs(int x){ size[x]=1; for(int e=head[x];e;e=E[e].next) dfs(E[e].to),size[x]+=size[E[e].to]; } inline void solve(int Ans=0){ memset(fa,0,sizeof(fa)); memset(head,0,sizeof(head)); n=gi();m=gi();tot=0; for(int i=1;i<=m;++i)fa[gi()]=gi(); for(int i=1;i<=n;++i)link(fa[i],i); dfs(0);Ans=J[n]; for(int i=1;i<=n;++i)Ans=1ll*Ans*Ny[size[i]]%Mod; printf("%d\n",(Ans+Mod)%Mod); } int main() { freopen(FILE".in","r",stdin); freopen(FILE".out","w",stdout); pre();int Case=gi();while(Case--)solve(); fclose(stdin);fclose(stdout); return 0; }