链接
https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=2115
题解
首先我建一个虚点,设其为森林中所有树的父亲,那现在就成一棵树了
设
f(x)
f
(
x
)
为以节点
x
x
为根的子树的答案
那么点本身肯定排在所有子节点的前面,现在只需考虑其子树中的点怎么排列,子树内部先排列,然后再把这些排列交错着插起来,子树之间是独立的,如果把同一棵子树上的点看作相同的点,那么这就是有重复元素的排列组合,假设子树的大小为
s(c1),s(c2)...s(ck)
s
(
c
1
)
,
s
(
c
2
)
.
.
.
s
(
c
k
)
,那么
其中 k k 是子节点个数,表示第 i i 个儿子,表示子树大小
使用代入法,就会发现当计算 f(x) f ( x ) 时,分子上会出现一个 [s(x)−1]! [ s ( x ) − 1 ] ! ,
在计算 f(father(x)) f ( f a t h e r ( x ) ) 时分母上会出现一个 s(x)! s ( x ) !
约分之后分母上剩下一个 s(x) s ( x )
而 [s(root)−1]! [ s ( r o o t ) − 1 ] ! 不会被约去,因为 root r o o t 没有父亲节点
这样最终答案就是
代码
//数学题
#include <bits/stdc++.h>
#define maxn 100010
#define mod 1000000007ll
#define cl(x) memset(x,0,sizeof(x))
#define ll long long
using namespace std;
ll head[maxn], to[maxn], nex[maxn], etot, N, M, mark[maxn], sz[maxn], ans, inv[maxn];
void adde(ll a, ll b){to[++etot]=b;nex[etot]=head[a];head[a]=etot;}
ll read(ll x=0)
{
ll c, f=1;
for(c=getchar();!isdigit(c);c=getchar())if(c=='-')f=-1;
for(;isdigit(c);c=getchar())x=(x<<1)+(x<<3)+c-48;
return f*x;
}
void preprocess()
{
ll i;
inv[1]=1;
for(i=2;i<maxn;i++)inv[i]=inv[mod%i]*(mod-mod/i)%mod;
}
void init()
{
ll i, a, b;
cl(head), cl(nex), etot=0, cl(mark), cl(sz), ans=1;
N=read(), M=read();
for(i=1;i<=M;i++)a=read(), b=read(), adde(b,a), mark[a]=1;
for(i=1;i<=N;i++)if(!mark[i])adde(N+1,i);
}
void dfs(ll pos)
{
ll p;
sz[pos]=1;
for(p=head[pos];p;p=nex[p])
{
dfs(to[p]);
sz[pos]+=sz[to[p]];
}
if(pos!=N+1)ans=ans*inv[sz[pos]]%mod;
}
void show()
{
ll i;
for(i=1;i<=N;i++)ans=ans*i%mod;
printf("%lld\n",ans);
}
int main()
{
ll T=read();
preprocess();
while(T--)
{
init();
dfs(N+1);
show();
}
return 0;
}