Stand in a Line
All the people in the byteland want to stand in a line in such a way that no person stands closer to the front of the line than his father. You are given the information about the people of the byteland. You have to determine the number of ways thebytelandian people can stand in a line.
Input
First line of the input contains T (T<14) the number of test case. Then following lines contains T Test cases.
Each test case starts with 2 integers n (1≤n≤40000) and m (0≤m
Output
For each test case the output contains a single line denoting the number of different ways the soldier can stand in a single line. The result may be too big. So always output the remainder on dividing ther the result by 1000000007.
Sample Input
3
3 2
2 1
3 1
3 0
3 1
2 1
Output for Sample Input
2
6
3
题意: 村民排队, 有n个人要求排成一列, 但是要求每个人不能排在他的父亲前面, 计算出有多少种排列方式.
其中有些人得父亲不在里面. 最后结果对1 000 000 007求余
解题思路: (一道好题!)
1. 森林问题, 只需要添加一个虚拟公共祖先就可以变成树了, 添加一个0虚拟公共祖先. 其余1~n编号
设: f[i]表示以i为根的子树排列数量, 每个节点数为s[i];
2. 现在以C为子树根的子树, ci为其孩子, 先考虑每课以孩子节点为根的子树的排列是相互独立的,
满足乘法原理, 所以不考虑子树之间的排列时排列总数为: f[c1]*f[c2]*f[c3]*...*f[ck];(k个孩子)
在把每棵子树中的全部节点看成相同的元素, 相当于有重复元素的全排列种数: n!/(n1!*n2!*...nk!)
即: f[C] = f[c1]*f[c2]*...*f[ck]*(s[C]-1)! / (s[c1]!*s[c2]!*...*s[ck]!);
将递归式化简得: f[root] = (s[root]-1)! / (s[1]*s[2]*...*s[n]);
3. 剩下的问题是如何快速求出表达式结果, f[root] % MOD的值, MOD是一个素数, 一个数除以x, 相当于
乘以x的逆.(群概念中的逆);
代码:
#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;
#define MAX 40005
#define MOD 1000000007
typedef long long ll;
struct node
{
int v;
int next;
}edges[MAX*2];
int n, m;
int first[MAX], num;
bool flag[MAX];
ll fac[MAX], inv[MAX];
ll ans;
void exgcd(ll a, ll b, ll &x, ll &y, ll &d)
{
if( b == 0 )
{
d = a;
x = 1;
y = 0;
}
else
{
exgcd(b, a%b, y, x, d);
y -= x*(a/b);
}
}
ll inverse(ll a, ll M)
{
ll x, y, d;
exgcd(a, M, x, y, d);
return d == 1 ? (x+M)%M : -1;
}
void init()
{
fac[0] = 1;
for(int i = 1; i <= 40000; ++i)
{
fac[i] = fac[i-1]*i % MOD;
inv[i] = inverse(i, MOD);
}
}
void add(int u, int v)
{
edges[num].v = v;
edges[num].next = first[u];
first[u] = num++;
}
int dfs(int u)
{
int cur = 1;
for(int e = first[u]; e != -1; e = edges[e].next)
cur += dfs(edges[e].v);
if(u != 0)
ans = ans * inv[cur] % MOD;
return cur;
}
int main()
{
// freopen("input.txt", "r", stdin);
init();
int caseNum, i;
scanf("%d", &caseNum);
while(caseNum--)
{
scanf("%d %d", &n, &m);
num = 1;
memset(first, -1, sizeof(first));
memset(flag, false, sizeof(flag));
int u, v;
for(i = 1; i <= m; ++i)
{
scanf("%d %d", &u, &v);
add(v, u);
flag[u] = true;
}
for(i = 1; i <= n; ++i)
if( !flag[i] ) add(0, i);
ans = fac[n];
dfs(0);
printf("%lld\n", ans);
}
return 0;
}