当前位置: 代码迷 >> 综合 >> UVA11174 Stand in a Line
  详细解决方案

UVA11174 Stand in a Line

热度:20   发布时间:2024-01-13 17:23:56.0

题意是:

村子里有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;
}


  相关解决方案