HDU 5379 树形DP Mahjong tree

任意一棵子树上节点的编号连续,每个节点的所有二字节点连续,求编号方案的总数。

稍微分析一下可知

  • 每个节点的非叶子节点个数不能多于两个,否则这个子树无解,从而整棵树都无解。
  • 每棵子树将所有节点按照编号从小到大排序,根节点要么在最左端,要么在最右端,而且这两种情况相等。(后面会有具体分析)

 

设size(u)表示以节点u为根的子树中节点总数。

d(u)表示用1 ~ size(u)给以u为根的子树编号的合法方案数,考虑下面几种情况:

 

①:  u是叶子节点,方案数为1.

 

②:  u的所有儿子节点都是叶子节点,那么所有儿子节点连续(假设儿子节点有S个),u只能放在所有儿子节点的前面或者最后面。所以方案数就是2(S!),因为儿子节点之间互不影响可以任意排列。

 

③:  u只有一个非叶儿子节点v。

方案数为:2 * d(v) * S!

首先子树v的排列方案有d(v)种,子树v的排列确定下来以后,而且v一定是在这个排列的某一端,由于所有儿子节点连续,所以这S个v的叶子兄弟必须紧挨着v,所以有S!种,此时只有u的位置没有确定好,u可以放在排列的开头或者末尾,所以答案最终要乘2.

 

④:  u有两个非叶子儿子节点v1, v2.

首先u的所有儿子节点是要连续的,所以v1, v2和他俩的叶子兄弟要在连续的一段;而且还要满足,v1这棵子树连续,v2这棵子树连续,所以v1和v2注定只能排在在u的儿子中的两端。

排列大概就是这样的,因为v1排在左端的方案数为d(v1) / 2,同样地v2排在右端的方案数为d(v2) / 2,叶子兄弟在v1和v2之间任意排列,u排在左右两端都行。

所以上图表示总的方案数为 d(v1) / 2 * d(v2) / 2 * S! * 2 = d(v1) * d(v2) / 2 * S!

不要急,还有一半情况没考虑到。就是v1子树排在右边,v2子树排在左边,所以上面的答案还要乘个2,得到 d(v1) * d(v2) * S!

 

⑤:  非叶子节点数多于两个,开头已经说过了,无解。

 

因为数据比较大,要手动扩栈然后用C++提交。

 1 #pragma comment(linker, "/STACK:102400000,102400000")
 2 #include <iostream>
 3 #include <cstdio>
 4 #include <cstring>
 5 #include <algorithm>
 6 #include <vector>
 7 using namespace std;
 8 
 9 typedef long long LL;
10 
11 const int maxn = 100000 + 10;
12 const LL MOD = 1000000007;
13 
14 vector<int> G[maxn];
15 
16 int sz[maxn];
17 LL d[maxn], fac[maxn];
18 
19 void dfs(int u, int fa)
20 {
21     sz[u] = 1;
22     LL T = 1;
23     int c1 = 0, c2 = 0;
24     for(int i = 0; i < G[u].size(); i++)
25     {
26         int v = G[u][i];
27         if(v == fa) continue;
28         dfs(v, u);
29         sz[u] += sz[v];
30 
31         if(d[v] == 0) { d[u] = 0; return ; }
32 
33         if(sz[v] > 1) { c1++; T = (T * d[v]) % MOD; }
34         else c2++;
35 
36         if(c1 > 2) { d[u] = 0; return ; }
37     }
38 
39     if(sz[u] == 1) { d[u] = 1; return ; }
40     if(c1 < 2) d[u] = (2LL * fac[c2] * T) % MOD;
41     else d[u] = (fac[c2] * T) % MOD;
42 }
43 
44 int main()
45 {
46     fac[0] = 1;
47     for(int i = 1; i < maxn; i++) fac[i] = (fac[i - 1] * i) % MOD;
48 
49     int T; scanf("%d", &T);
50     for(int kase = 1; kase <= T; kase++)
51     {
52         int n; scanf("%d", &n);
53         for(int i = 1; i <= n; i++) G[i].clear();
54         for(int i = 1; i < n; i++)
55         {
56             int u, v; scanf("%d%d", &u, &v);
57             G[u].push_back(v);
58             G[v].push_back(u);
59         }
60 
61         printf("Case #%d: ", kase);
62 
63         if(n == 1) { puts("1"); continue; }
64 
65         dfs(1, 0);
66         printf("%I64d\n", d[1]);
67     }
68 
69     return 0;
70 }
代码君

 

posted @ 2015-08-14 14:28  AOQNRMGYXLMV  阅读(175)  评论(0编辑  收藏  举报