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

UVa 11174 (乘法逆元) Stand in a Line

时间:2015-03-02 23:40:15      阅读:196      评论:0      收藏:0      [点我收藏+]

标签:

题意:

有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

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