uva 11174 Stand in a Line

时间:2021-12-28 09:05:55
//    uva 11174 Stand in a Line
//
// 题目大意:
//
// 村子有n个村民,有多少种方法,使村民排成一条线
// 使得没有人站在他父亲的前面.
//
// 解题思路:
//
// 换成模型,先将森林变成一棵树,这样就直观多了,对于
// 一个节点,他的子节点排列时没有任何要求,而子排列中会有
// 限制,将这些限制先提取出来,就可以将所有的视为相同的了,
// 然后就是有重复元素的全排列问题.设s(i)为以i节点为根的子树
// f(i)为以i为根的子树的排法,
// f(i) = f(c1)f(c2)...(s(i)-1)! / (s(c1)!s(c2)!...s(ck)!);
// cj为i的第j个儿子.递归发现所有的非根节点的f带入上式,发现所有的
// 非根节点u都会以(s(u)-1)!出现在以他为根的分子中,s(u)出现在他父
// 节点的分母中,约分之后,分子为1,分母为s(u),这样就得到了一个
// 简化的式子,:
// f(root) = (s(root)-1)!/(s(c1)s(c2)...s(ck));
// s(root) = n + 1;
// 最后的结果为n!除以所有的s(i). #include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector> using namespace std; const int MAX_N = ; typedef long long ll; vector<int> g[MAX_N]; const ll MOD = 1e9 + ; int cnt[MAX_N];
bool vis[MAX_N];
int n,m; void dfs(int u){
vis[u] = ;
cnt[u]=;
for (int i=;i<g[u].size();i++){
int v = g[u][i];
if (vis[v])
continue; dfs(v); cnt[u] += cnt[v];
}
} void exgcd(ll a,ll b,ll &x,ll &y){
if (b==){
x = ;
y = ;
return ;
}
exgcd(b,a%b,x,y);
ll temp = x;
x = y;
y = temp - a / b * y;
} void print(){
for (int i=;i<=n;i++){
printf("%d ",cnt[i]);
}
cout << endl;
} void input(){
scanf("%d%d",&n,&m);
ll ans = ;
memset(cnt,,sizeof(cnt));
memset(vis,,sizeof(vis));
for (int i=;i<=n;i++){
ans = ans * i % MOD;
g[i].clear();
}
int u,v;
for (int i=;i<=m;i++){
scanf("%d%d",&u,&v);
g[u].push_back(v);
g[v].push_back(u);
} for (int i=;i<=n;i++){
if (!vis[i])
dfs(i);
}
// print();
ll p = ; for (int i=;i<=n;i++){
p = p * cnt[i] % MOD;
} //printf("cnt = %lld\n",p); ll x,y; exgcd(MOD,p,x,y); cout << ans * ((y + MOD )%MOD) % MOD << endl; } int main(){
int t;
// freopen("1.txt","r",stdin);
scanf("%d",&t);
while(t--){
input();
}
}