题意是:
村子里有n个人,有多少种方式可以把他们排成一列,使得没人会排在他父亲的前面
然后给的关系大概是一个森林的样子
看训练指南中的讲解才豁然开朗
设f(i)是以节点i为根的子树的排列方法数
则 f(i) = f(c1)* f(c2) *f(c3) * f(c4).....* f(ck) * (s(i) - 1)! / ((s(c1)! * (s(c2))! .... * (s(ck))!)
s(i)表示的是以i为根的子树的节点个数
c1, c2,...ck为i的儿子们
这个式子首先是把各个子树的都排列好
然后子树之间,就类似有重复元素的全排列
然后观察这个式子
将每个非根节点带入上式子
可发现每个非根节点u以(s(u) - 1)!的形式出现在分子一次
以s(u)的形式出现在分母一次。
约分后相当于分子为1,分母为s(u)
得到最终的式子是、
f(root) = (s(root)-1)!/(s(i) * s(2) *... *s(n))
注意原图是个森林
所以我们可以建立一个超级祖先,将所有的根连上去
就成了一个根了
那么其实最终的解就是
n!/(s(1) * s(2) * ... * s(k))
那么因为我们要mod一个数
所以就要边算逆元边mod了
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <string>
#include <map>
#include <vector>
#include <algorithm>
#define INF 111111111
#define MAXN 40005
#define MAXM 900005
using namespace std;
long long mod = 1000000007;
long long fac[MAXN];
vector<int>g[MAXN];
int son[MAXN];
int sz[MAXN];
int n, m;
long long fastmod(long long a, long long b, long long c)
{
long long ret = 1;
a %= c;
for(; b; b >>= 1, a = (a * a) % c)
if(b & 1)
ret = ret * a % c;
return ret;
}
void dfs(int u)
{
sz[u] = 1;
for(int i = 0; i < g[u].size(); i++)
{
dfs(g[u][i]);
sz[u] += sz[g[u][i]];
}
}
int main()
{
fac[0] = 1;
for(int i = 1; i < MAXN; i++) fac[i] = fac[i - 1] * (long long)i % mod;
int T, u, v;
scanf("%d", &T);
while(T--)
{
memset(son, 0, sizeof(son));
scanf("%d%d", &n, &m);
for(int i = 0; i <= n; i++) g[i].clear();
for(int i = 0; i < m; i++)
{
scanf("%d%d", &u, &v);
son[u] = 1;
g[v].push_back(u);
}
for(int i = 1; i <= n; i++)
{
if(!son[i])
dfs(i);
}
long long ans = fac[n];
for(int i = 1; i <= n; i++)
{
long long k = fastmod(sz[i], mod - 2, mod);
ans = ans * k % mod;
}
printf("%lld\n", ans);
}
return 0;
}