题意:n个人排队,m条父子关系,要求父亲一定要排在儿子前面(不一定要相邻),问最多能有多少种排法?
思路:父亲一定要排在儿子前面,也就是说父亲和儿子的位置是不可以调换的,那么我们不妨把父亲和儿子看成是同一个数字,例如
2是4和5的父亲,3是6的父亲,那么我们不妨把这5个人看成是22233在排队,那么总共的排法就是5!/(3!*2!);要注意的是每个平行的父亲之间,他们的子树是符合乘法原理的,对于没有父亲的人我们就给一个虚拟的父亲0,这样就方便我们建树啦~因为阶乘会非常的大,所以我们无法直接求得a/b的值,那么就用乘法逆元好了~(不知道的可以去看一下扩展欧几里得),计数可以参考大白书P103页。
代码如下:
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
#define maxn 40005
#define mod 1000000007
#define ll long long
ll jc[maxn], arr[maxn],vis[maxn], s[maxn];
int t, m, n, a, b;
vector<int>G[maxn];
void gcd(ll a, ll b, ll& d, ll& x, ll& y)
{
if(!b)
{
d=a;x=1;y=0;
}
else
{
gcd(b, a%b, d, y, x);
y-=x*(a/b);
}
}
ll f(ll a, ll n)
{
ll d,x,y;
gcd(a,n,d,x,y);
return d==1?(x+n)%n:-1;
}
int dfs(int u)
{
int num=0;
for(int i=0; i<G[u].size(); i++)
{
num+=dfs(G[u][i]);
}
vis[u]=1;
s[u]=++num;
return s[u];
}
int main()
{
jc[0]=jc[1]=arr[1]=1;
for(int i=2; i<maxn; i++)
{
jc[i]=(jc[i-1]*i)%mod;
arr[i]=f(i, mod);
}
scanf("%d", &t);
while(t--)
{
scanf("%d%d", &n, &m);
memset(vis, 0, sizeof(vis));
for(int i=1; i<=n; i++)
G[i].clear();
for(int i=1; i<=m; i++)
{
scanf("%d%d", &a, &b);
G[b].push_back(a);
}
for(int i=1; i<=n; i++)
{
if(!vis[i]) dfs(i);
}
ll ans=jc[n];
for(int i=1; i<=n; i++)
{
ans=(ans*arr[s[i]])%mod;
}
printf("%lld\n", ans);
}
return 0;
}UVA11174 J.Stand in a Line (计数+逆元)
原文:http://blog.csdn.net/doris1104/article/details/45540899