标签: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