标签:
// 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 = 40008; typedef long long ll; vector<int> g[MAX_N]; const ll MOD = 1e9 + 7; int cnt[MAX_N]; bool vis[MAX_N]; int n,m; void dfs(int u){ vis[u] = 1; cnt[u]=1; for (int i=0;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==0){ x = 1; y = 0; return ; } exgcd(b,a%b,x,y); ll temp = x; x = y; y = temp - a / b * y; } void print(){ for (int i=1;i<=n;i++){ printf("%d ",cnt[i]); } cout << endl; } void input(){ scanf("%d%d",&n,&m); ll ans = 1; memset(cnt,0,sizeof(cnt)); memset(vis,0,sizeof(vis)); for (int i=1;i<=n;i++){ ans = ans * i % MOD; g[i].clear(); } int u,v; for (int i=1;i<=m;i++){ scanf("%d%d",&u,&v); g[u].push_back(v); g[v].push_back(u); } for (int i=1;i<=n;i++){ if (!vis[i]) dfs(i); } // print(); ll p = 1; for (int i=1;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(); } }
标签:
原文地址:http://www.cnblogs.com/KingJourney/p/4705621.html