码迷,mamicode.com
首页 > 其他好文 > 详细

UVA11174 J.Stand in a Line (计数+逆元)

时间:2015-05-06 23:04:27      阅读:206      评论:0      收藏:0      [点我收藏+]

标签:uva

题意: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 (计数+逆元)

标签:uva

原文地址:http://blog.csdn.net/doris1104/article/details/45540899

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!