标签:
题意:
有n个人排队,要求每个人不能排在自己父亲的前面(如果有的话),求所有的排队方案数模1e9+7的值。
分析:
《训练指南》上分析得挺清楚的,把公式贴一下吧:
设f(i)为以i为根节点的子树的排列方法,s(i)表示以i为根的子树的节点总数。
f(i) = f(c1)f(c2)...f(ck)×(s(i)-1)!/(s(c1)!s(c2)!...s(ck)!)
按照书上最开始举的例子,其实这个式子也不难理解,就是先给这些子树确定一下位置,即有重元素的全排列。
子树的位置确定好以后,然后再确定子树中各个节点的顺序。
对了,因为求组合数会用到除法,而且1e9+7又是个素数,所以我们在做除法的时候只要乘上它对应的乘法逆元即可。
这是递归计算的代码:
1 #include <bits/stdc++.h> 2 3 using namespace std; 4 5 const int maxn = 40000 + 10; 6 const int MOD = 1000000007; 7 8 vector<int> sons[maxn]; 9 int fa[maxn], fac[maxn], ifac[maxn]; 10 11 inline int mul_mod(int a, int b, int n = MOD) 12 { 13 a %= n; b %= n; 14 return (int)((long long)a * b % n); 15 } 16 17 void gcd(int a, int b, int& d, int& x, int& y) 18 { 19 if(!b) { d = a; x = 1; y = 0; } 20 else{ gcd(b, a%b, d, y, x); y -= x*(a/b); } 21 } 22 23 int inv(int a, int n) 24 { 25 int d, x, y; 26 gcd(a, n, d, x, y); 27 return d == 1 ? (x+n)%n : -1; 28 } 29 30 int C(int n, int m) 31 { return mul_mod(mul_mod(fac[n], ifac[m]), ifac[n-m]); } 32 33 void init() 34 { 35 fac[0] = ifac[0] = 1; 36 for(int i = 1; i < maxn; i++) 37 { 38 fac[i] = mul_mod(fac[i - 1], i); 39 ifac[i] = inv(fac[i], MOD); 40 } 41 } 42 43 int count(int u, int& size)//size是u为根的子树的节点总数 44 {//统计u为根的子树的排列方案 45 size = 1; 46 int ans = 1; 47 int d = sons[u].size(); 48 vector<int> sonsize; 49 for(int i = 0; i < d; i++) 50 { 51 int sz; 52 ans = mul_mod(ans, count(sons[u][i], sz)); 53 size += sz; 54 sonsize.push_back(sz); 55 } 56 int sz = size - 1; 57 for(int i = 0; i < d; i++) 58 { 59 ans = mul_mod(ans, C(sz, sonsize[i])); 60 sz -= sonsize[i]; 61 } 62 return ans; 63 } 64 65 int main() 66 { 67 //freopen("in.txt", "r", stdin); 68 69 init(); 70 int T; 71 scanf("%d", &T); 72 while(T--) 73 { 74 int n, m; 75 scanf("%d%d", &n, &m); 76 memset(fa, 0, sizeof(fa)); 77 for(int i = 0; i <= n; i++) sons[i].clear(); 78 for(int i = 0; i < m; i++) 79 { 80 int a, b; 81 scanf("%d%d", &a, &b); 82 fa[a] = b; 83 sons[b].push_back(a); 84 } 85 for(int i = 1; i <= n; i++) 86 if(!fa[i]) sons[0].push_back(i); 87 int size; 88 printf("%d\n", count(0, size)); 89 } 90 91 return 0; 92 }
书上后面又提到如果将上式完全展开,每个非根节点u都以(s(u)-1)!出现在分子一次,又以s(u)!出现在分母一次,约分后就只剩分母一个s(u)了。
这样f(root) = (s(root)-1)!/(s(1)s(2)...s(n)),又因为s(root) = n+1(因为除了n个人还有一个虚拟的0祖宗节点),
所以f(root) = n!/(s(1)s(2)...s(n)),还是用递归算出所有的s(i)
这样预处理一下40000以内的阶乘和乘法逆元即可。
1 #include <bits/stdc++.h> 2 3 using namespace std; 4 5 const int maxn = 40000 + 10; 6 const int MOD = 1000000007; 7 8 int n, m; 9 vector<int> sons[maxn]; 10 int fa[maxn], fac[maxn], ifac[maxn], inverse[maxn], sonsize[maxn]; 11 12 inline int mul_mod(int a, int b, int n = MOD) 13 { 14 a %= n; b %= n; 15 return (int)((long long)a * b % n); 16 } 17 18 void gcd(int a, int b, int& d, int& x, int& y) 19 { 20 if(!b) { d = a; x = 1; y = 0; } 21 else{ gcd(b, a%b, d, y, x); y -= x*(a/b); } 22 } 23 24 int inv(int a, int n = MOD) 25 { 26 int d, x, y; 27 gcd(a, n, d, x, y); 28 return d == 1 ? (x+n)%n : -1; 29 } 30 31 int C(int n, int m) 32 { return mul_mod(mul_mod(fac[n], ifac[m]), ifac[n-m]); } 33 34 void init() 35 { 36 fac[0] = ifac[0] = 1; 37 for(int i = 1; i < maxn; i++) 38 { 39 fac[i] = mul_mod(fac[i - 1], i); 40 inverse[i] = inv(i); 41 } 42 } 43 44 void count(int u, int& size) 45 {//统计以u为根的子树节点个数 46 size = 1; 47 int d = sons[u].size(); 48 for(int i = 0; i < d; i++) 49 { 50 int sz; 51 count(sons[u][i], sz); 52 size += sz; 53 } 54 sonsize[u] += size; 55 } 56 57 int main() 58 { 59 //freopen("in.txt", "r", stdin); 60 61 init(); 62 int T; 63 scanf("%d", &T); 64 while(T--) 65 { 66 memset(fa, 0, sizeof(fa)); 67 memset(sonsize, 0, sizeof(sonsize)); 68 for(int i = 0; i <= n; i++) sons[i].clear(); 69 70 scanf("%d%d", &n, &m); 71 for(int i = 0; i < m; i++) 72 { 73 int a, b; 74 scanf("%d%d", &a, &b); 75 fa[a] = b; 76 sons[b].push_back(a); 77 } 78 for(int i = 1; i <= n; i++) 79 if(!fa[i]) sons[0].push_back(i); 80 int size; 81 count(0, size); 82 //for(int i = 1; i <= n; i++) printf("%d\n", sonsize[i]); 83 int ans = fac[n]; 84 for(int i = 1; i <= n; i++) ans = mul_mod(ans, inverse[sonsize[i]]); 85 printf("%d\n", ans); 86 } 87 88 return 0; 89 }
UVa 11174 (乘法逆元) Stand in a Line
标签:
原文地址:http://www.cnblogs.com/AOQNRMGYXLMV/p/4309868.html